* [PATCH 2/2] Input: pm8941-powerkey - fix debounce on gen2+ PMICs
From: Caleb Connolly @ 2023-05-29 19:55 UTC (permalink / raw)
To: Dmitry Torokhov, Andy Gross, Bjorn Andersson, Konrad Dybcio
Cc: linux-input, linux-arm-msm, phone-devel, Caleb Connolly
In-Reply-To: <20230529-pm8941-pwrkey-debounce-v1-0-c043a6d5c814@linaro.org>
Since PM8998/PM660, the power key debounce register was redefined to
support shorter debounce times. On PM8941 the shortest debounce time
(represented by register value 0) was 15625us, on PM8998 the shortest
debounce time is 62us, with the default being 2ms.
Adjust the bit shift to correctly program debounce on PM8998 and newer.
Fixes: 68c581d5e7d8 ("Input: add Qualcomm PM8941 power key driver")
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
---
This patch shouldn't be backported earlier then 5.4, as that is the
first kernel with support for PM8998.
---
drivers/input/misc/pm8941-pwrkey.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
index b6a27ebae977..74d77d8aaeff 100644
--- a/drivers/input/misc/pm8941-pwrkey.c
+++ b/drivers/input/misc/pm8941-pwrkey.c
@@ -50,7 +50,10 @@
#define PON_RESIN_PULL_UP BIT(0)
#define PON_DBC_CTL 0x71
-#define PON_DBC_DELAY_MASK 0x7
+#define PON_DBC_DELAY_MASK_GEN1 0x7
+#define PON_DBC_DELAY_MASK_GEN2 0xf
+#define PON_DBC_SHIFT_GEN1 6
+#define PON_DBC_SHIFT_GEN2 14
struct pm8941_data {
unsigned int pull_up_bit;
@@ -247,7 +250,7 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
struct device *parent;
struct device_node *regmap_node;
const __be32 *addr;
- u32 req_delay;
+ u32 req_delay, mask, delay_shift;
int error;
if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
@@ -336,12 +339,20 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
pwrkey->input->phys = pwrkey->data->phys;
if (pwrkey->data->supports_debounce_config) {
- req_delay = (req_delay << 6) / USEC_PER_SEC;
+ if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY) {
+ mask = PON_DBC_DELAY_MASK_GEN2;
+ delay_shift = PON_DBC_SHIFT_GEN2;
+ } else {
+ mask = PON_DBC_DELAY_MASK_GEN1;
+ delay_shift = PON_DBC_SHIFT_GEN1;
+ }
+
+ req_delay = (req_delay << delay_shift) / USEC_PER_SEC;
req_delay = ilog2(req_delay);
error = regmap_update_bits(pwrkey->regmap,
pwrkey->baseaddr + PON_DBC_CTL,
- PON_DBC_DELAY_MASK,
+ mask,
req_delay);
if (error) {
dev_err(&pdev->dev, "failed to set debounce: %d\n",
--
2.40.1
^ permalink raw reply related
* [PATCH RFC v1 0/3] HID: nvidia-shield: More functionality on top of initial driver
From: Rahul Rameshbabu @ 2023-05-29 22:20 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: linux-input, linux-kernel, Rahul Rameshbabu
This patch series introduces more functionality on top of the initial
nvidia-shield HID driver sent to the linux-input mailing list. The android
media buttons, capacitive slider, and led on the Thunderstrike controller
are now supported. If possible, would appreciate an acknowledgment of the
initial driver patch or more feedback if needed.
Link: https://lore.kernel.org/linux-input/20230418203747.24000-1-rrameshbabu@nvidia.com/
Rahul Rameshbabu (3):
HID: nvidia-shield: Add mappings for consumer HID USAGE buttons
HID: nvidia-shield: Support LED functionality for Thunderstrike
HID: nvidia-shield: Remove space prefix from label in
shield_haptics_create
drivers/hid/hid-nvidia-shield.c | 173 ++++++++++++++++++++++++++++++--
1 file changed, 162 insertions(+), 11 deletions(-)
--
2.38.4
^ permalink raw reply
* [PATCH RFC v1 1/3] HID: nvidia-shield: Add mappings for consumer HID USAGE buttons
From: Rahul Rameshbabu @ 2023-05-29 22:20 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: linux-input, linux-kernel, Rahul Rameshbabu
In-Reply-To: <20230529222052.68913-1-rrameshbabu@nvidia.com>
Map Android Home, Back, Search, VolumeUp, VolumeDown, and PlayPause buttons
to the appropriate input event codes.
Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
---
drivers/hid/hid-nvidia-shield.c | 55 ++++++++++++++++++++++++++++++---
1 file changed, 50 insertions(+), 5 deletions(-)
diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c
index bcf345e365c0..1a9d32b2c80c 100644
--- a/drivers/hid/hid-nvidia-shield.c
+++ b/drivers/hid/hid-nvidia-shield.c
@@ -15,6 +15,16 @@
#include "hid-ids.h"
#define NOT_INIT_STR "NOT INITIALIZED"
+#define android_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
+
+enum {
+ HID_USAGE_ANDROID_PLAYPAUSE_BTN = 0xcd, /* Double-tap volume slider */
+ HID_USAGE_ANDROID_VOLUMEUP_BTN = 0xe9,
+ HID_USAGE_ANDROID_VOLUMEDOWN_BTN = 0xea,
+ HID_USAGE_ANDROID_SEARCH_BTN = 0x221, /* NVIDIA btn on Thunderstrike */
+ HID_USAGE_ANDROID_HOME_BTN = 0x223,
+ HID_USAGE_ANDROID_BACK_BTN = 0x224,
+};
enum {
SHIELD_FW_VERSION_INITIALIZED = 0,
@@ -416,6 +426,40 @@ static struct shield_device *thunderstrike_create(struct hid_device *hdev)
return shield_dev;
}
+static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field,
+ struct hid_usage *usage, unsigned long **bit,
+ int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+ return 0;
+
+ switch (usage->hid & HID_USAGE) {
+ case HID_USAGE_ANDROID_PLAYPAUSE_BTN:
+ android_map_key(KEY_PLAYPAUSE);
+ break;
+ case HID_USAGE_ANDROID_VOLUMEUP_BTN:
+ android_map_key(KEY_VOLUMEUP);
+ break;
+ case HID_USAGE_ANDROID_VOLUMEDOWN_BTN:
+ android_map_key(KEY_VOLUMEDOWN);
+ break;
+ case HID_USAGE_ANDROID_SEARCH_BTN:
+ android_map_key(BTN_Z);
+ break;
+ case HID_USAGE_ANDROID_HOME_BTN:
+ android_map_key(BTN_MODE);
+ break;
+ case HID_USAGE_ANDROID_BACK_BTN:
+ android_map_key(BTN_SELECT);
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -571,11 +615,12 @@ static const struct hid_device_id shield_devices[] = {
MODULE_DEVICE_TABLE(hid, shield_devices);
static struct hid_driver shield_driver = {
- .name = "shield",
- .id_table = shield_devices,
- .probe = shield_probe,
- .remove = shield_remove,
- .raw_event = shield_raw_event,
+ .name = "shield",
+ .id_table = shield_devices,
+ .input_mapping = android_input_mapping,
+ .probe = shield_probe,
+ .remove = shield_remove,
+ .raw_event = shield_raw_event,
.driver = {
.dev_groups = shield_device_groups,
},
--
2.38.4
^ permalink raw reply related
* [PATCH RFC v1 2/3] HID: nvidia-shield: Support LED functionality for Thunderstrike
From: Rahul Rameshbabu @ 2023-05-29 22:20 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: linux-input, linux-kernel, Rahul Rameshbabu
In-Reply-To: <20230529222052.68913-1-rrameshbabu@nvidia.com>
Expose the 2017 SHIELD controller (Thunderstrike) LED through the kernel
LED API.
Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
---
drivers/hid/hid-nvidia-shield.c | 116 ++++++++++++++++++++++++++++++--
1 file changed, 111 insertions(+), 5 deletions(-)
diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c
index 1a9d32b2c80c..7dfaec1c07e2 100644
--- a/drivers/hid/hid-nvidia-shield.c
+++ b/drivers/hid/hid-nvidia-shield.c
@@ -8,6 +8,7 @@
#include <linux/hid.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
+#include <linux/leds.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@@ -35,6 +36,7 @@ enum {
THUNDERSTRIKE_FW_VERSION_UPDATE = 0,
THUNDERSTRIKE_BOARD_INFO_UPDATE,
THUNDERSTRIKE_HAPTICS_UPDATE,
+ THUNDERSTRIKE_LED_UPDATE,
};
enum {
@@ -45,12 +47,19 @@ enum {
enum {
THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1,
+ THUNDERSTRIKE_HOSTCMD_ID_LED = 6,
THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16,
THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53,
THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57,
THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT = 58,
};
+enum thunderstrike_led_state {
+ THUNDERSTRIKE_LED_OFF = 1,
+ THUNDERSTRIKE_LED_ON = 8,
+} __packed;
+static_assert(sizeof(enum thunderstrike_led_state) == 1);
+
struct thunderstrike_hostcmd_board_info {
__le16 revision;
__le16 serial[7];
@@ -70,6 +79,7 @@ struct thunderstrike_hostcmd_resp_report {
struct thunderstrike_hostcmd_board_info board_info;
struct thunderstrike_hostcmd_haptics motors;
__le16 fw_version;
+ enum thunderstrike_led_state led_state;
u8 payload[30];
};
} __packed;
@@ -81,10 +91,16 @@ struct thunderstrike_hostcmd_req_report {
u8 cmd_id;
u8 reserved_at_10;
- struct {
- u8 update;
- struct thunderstrike_hostcmd_haptics motors;
- } haptics;
+ union {
+ struct {
+ u8 update;
+ enum thunderstrike_led_state state;
+ } led;
+ struct {
+ u8 update;
+ struct thunderstrike_hostcmd_haptics motors;
+ } haptics;
+ };
u8 reserved_at_30[27];
} __packed;
static_assert(sizeof(struct thunderstrike_hostcmd_req_report) ==
@@ -108,12 +124,15 @@ struct thunderstrike {
/* Sub-devices */
struct input_dev *haptics_dev;
+ struct led_classdev led_dev;
/* Resources */
void *req_report_dmabuf;
unsigned long update_flags;
struct thunderstrike_hostcmd_haptics haptics_val;
spinlock_t haptics_update_lock;
+ u8 led_state : 1;
+ enum thunderstrike_led_state led_value;
struct work_struct hostcmd_req_work;
};
@@ -221,6 +240,13 @@ static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work)
thunderstrike_send_hostcmd_request(ts);
}
+ if (test_and_clear_bit(THUNDERSTRIKE_LED_UPDATE, &ts->update_flags)) {
+ thunderstrike_hostcmd_req_report_init(report, THUNDERSTRIKE_HOSTCMD_ID_LED);
+ report->led.update = 1;
+ report->led.state = ts->led_value;
+ thunderstrike_send_hostcmd_request(ts);
+ }
+
if (test_and_clear_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags)) {
thunderstrike_hostcmd_req_report_init(
report, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO);
@@ -292,6 +318,40 @@ static int thunderstrike_play_effect(struct input_dev *idev, void *data,
return thunderstrike_update_haptics(ts, &motors);
}
+static enum led_brightness
+thunderstrike_led_get_brightness(struct led_classdev *led)
+{
+ struct hid_device *hdev = to_hid_device(led->dev->parent);
+ struct shield_device *shield_dev = hid_get_drvdata(hdev);
+ struct thunderstrike *ts;
+
+ ts = container_of(shield_dev, struct thunderstrike, base);
+
+ return ts->led_state;
+}
+
+static void thunderstrike_led_set_brightness(struct led_classdev *led,
+ enum led_brightness value)
+{
+ struct hid_device *hdev = to_hid_device(led->dev->parent);
+ struct shield_device *shield_dev = hid_get_drvdata(hdev);
+ struct thunderstrike *ts;
+
+ ts = container_of(shield_dev, struct thunderstrike, base);
+
+ switch (value) {
+ case LED_OFF:
+ ts->led_value = THUNDERSTRIKE_LED_OFF;
+ break;
+ default:
+ ts->led_value = THUNDERSTRIKE_LED_ON;
+ break;
+ }
+
+ set_bit(THUNDERSTRIKE_LED_UPDATE, &ts->update_flags);
+ schedule_work(&ts->hostcmd_req_work);
+}
+
static void
thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev,
__le16 fw_version)
@@ -338,6 +398,24 @@ thunderstrike_parse_haptics_payload(struct shield_device *shield_dev,
haptics->motor_left, haptics->motor_right);
}
+static void
+thunderstrike_parse_led_payload(struct shield_device *shield_dev,
+ enum thunderstrike_led_state led_state)
+{
+ struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+
+ switch (led_state) {
+ case THUNDERSTRIKE_LED_OFF:
+ ts->led_state = 0;
+ break;
+ case THUNDERSTRIKE_LED_ON:
+ ts->led_state = 1;
+ break;
+ }
+
+ hid_dbg(shield_dev->hdev, "Thunderstrike led HOSTCMD response, 0x%02X\n", led_state);
+}
+
static int thunderstrike_parse_report(struct shield_device *shield_dev,
struct hid_report *report, u8 *data,
int size)
@@ -364,6 +442,9 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
thunderstrike_parse_fw_version_payload(
shield_dev, hostcmd_resp_report->fw_version);
break;
+ case THUNDERSTRIKE_HOSTCMD_ID_LED:
+ thunderstrike_parse_led_payload(shield_dev, hostcmd_resp_report->led_state);
+ break;
case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO:
thunderstrike_parse_board_info_payload(
shield_dev, &hostcmd_resp_report->board_info);
@@ -395,10 +476,24 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
return 0;
}
+static inline int thunderstrike_led_create(struct thunderstrike *ts)
+{
+ struct led_classdev *led = &ts->led_dev;
+
+ led->name = "thunderstrike:blue:led";
+ led->max_brightness = 1;
+ led->flags = LED_CORE_SUSPENDRESUME;
+ led->brightness_get = &thunderstrike_led_get_brightness;
+ led->brightness_set = &thunderstrike_led_set_brightness;
+
+ return led_classdev_register(&ts->base.hdev->dev, led);
+}
+
static struct shield_device *thunderstrike_create(struct hid_device *hdev)
{
struct shield_device *shield_dev;
struct thunderstrike *ts;
+ int ret;
ts = devm_kzalloc(&hdev->dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
@@ -418,12 +513,22 @@ static struct shield_device *thunderstrike_create(struct hid_device *hdev)
hid_set_drvdata(hdev, shield_dev);
+ ret = thunderstrike_led_create(ts);
+ if (ret) {
+ hid_err(hdev, "Failed to create Thunderstrike LED instance\n");
+ return ERR_PTR(ret);
+ }
+
ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
if (IS_ERR(ts->haptics_dev))
- return ERR_CAST(ts->haptics_dev);
+ goto err;
hid_info(hdev, "Registered Thunderstrike controller\n");
return shield_dev;
+
+err:
+ led_classdev_unregister(&ts->led_dev);
+ return ERR_CAST(ts->haptics_dev);
}
static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -599,6 +704,7 @@ static void shield_remove(struct hid_device *hdev)
ts = container_of(dev, struct thunderstrike, base);
hid_hw_close(hdev);
+ led_classdev_unregister(&ts->led_dev);
if (ts->haptics_dev)
input_unregister_device(ts->haptics_dev);
cancel_work_sync(&ts->hostcmd_req_work);
--
2.38.4
^ permalink raw reply related
* [PATCH RFC v1 3/3] HID: nvidia-shield: Remove space prefix from label in shield_haptics_create
From: Rahul Rameshbabu @ 2023-05-29 22:20 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: linux-input, linux-kernel, Rahul Rameshbabu
In-Reply-To: <20230529222052.68913-1-rrameshbabu@nvidia.com>
Accidentally had a whitespace character in front of a label used for error
handling in the shield_haptics_create function.
Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
---
Notes:
This patch really should be updated in the initial patch that introduces
the nvidia-shield HID driver. Using a separate patch to just illustrate
this is a cosmetic change if needing to resubmit the original patch for
nvidia-shield.
Link: https://lore.kernel.org/linux-input/20230418203747.24000-1-rrameshbabu@nvidia.com/
drivers/hid/hid-nvidia-shield.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c
index 7dfaec1c07e2..85700cec5eac 100644
--- a/drivers/hid/hid-nvidia-shield.c
+++ b/drivers/hid/hid-nvidia-shield.c
@@ -202,7 +202,7 @@ static struct input_dev *shield_haptics_create(
return haptics;
- err:
+err:
input_free_device(haptics);
return ERR_PTR(ret);
}
--
2.38.4
^ permalink raw reply related
* [PATCH 1/1] HID: logitech-hidpp: Add USB and Bluetooth IDs for the Logitech G915 TKL Keyboard
From: Stuart Hayhurst @ 2023-05-29 23:30 UTC (permalink / raw)
To: jikos
Cc: stuarthayhurst, Filipe Laíns, Benjamin Tissoires,
linux-input, linux-kernel
From: stuarthayhurst <stuart.a.hayhurst@gmail.com>
Adds the USB and Bluetooth IDs for the Logitech G915 TKL keyboard
---
drivers/hid/hid-logitech-hidpp.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index b2cd7527de19..28761272afe5 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -4403,6 +4403,8 @@ static const struct hid_device_id hidpp_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
{ /* Logitech G903 Hero Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) },
+ { /* Logitech G915 TKL Keyboard over USB */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC343) },
{ /* Logitech G920 Wheel over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
@@ -4418,6 +4420,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* MX5500 keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
+ { /* Logitech G915 TKL keyboard over Bluetooth */
+ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb35f) },
{ /* M-RCQ142 V470 Cordless Laser Mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
{ /* MX Master mouse over Bluetooth */
--
2.40.1.521.gf1e218fcd8
^ permalink raw reply related
* [PATCH v2 0/2] Add support for Azoteq IQS7210A/7211A/E
From: Jeff LaBundy @ 2023-05-30 0:31 UTC (permalink / raw)
To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, jeff
This series introduces support for the Azoteq IQS7210A, IQS7211A and
IQS7211E trackpad/touchscreen controllers. Optimized for wearable
applications, these devices track up to two contacts and are capable
of reporting an array of gestures.
The IQS7210A includes an additional channel to support an inductive
or capacitive button, while the IQS7211E adds an advanced assortment
of gestures. All three devices are demonstrated in [1].
These devices can be configured during production using OTP memory,
or over I2C using the device tree. For the latter case, the binding
covers all major parameters called out by the vendor in [2].
[1] https://youtu.be/RjB8rNkzQJQ
[2] https://www.azoteq.com/images/stories/pdf/azd123_iqs721xy_trackpad_userguide.pdf
Jeff LaBundy (2):
dt-bindings: input: Add bindings for Azoteq IQS7210A/7211A/E
Input: add support for Azoteq IQS7210A/7211A/E
.../input/touchscreen/azoteq,iqs7211.yaml | 769 +++++
drivers/input/touchscreen/Kconfig | 10 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/iqs7211.c | 2569 +++++++++++++++++
4 files changed, 3349 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml
create mode 100644 drivers/input/touchscreen/iqs7211.c
--
2.34.1
^ permalink raw reply
* [PATCH v2 1/2] dt-bindings: input: Add bindings for Azoteq IQS7210A/7211A/E
From: Jeff LaBundy @ 2023-05-30 0:33 UTC (permalink / raw)
To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, jeff
In-Reply-To: <ZHVD/9OgRTAwBhqx@nixie71>
Add bindings for the Azoteq IQS7210A/7211A/E family of trackpad/
touchscreen controllers.
Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
- Renamed 'azoteq,default-comms' to 'azoteq,forced-comms-default' and redefined
0, 1 and 2 as unspecified, 0 and 1, respectively
- Defined ATI upon its first occurrence
- Redefined 'azoteq,gesture-angle' in units of degrees
- Declared 'azoteq,rx-enable' to depend upon 'azoteq,tx-enable' within the
'trackpad' node
Hi Rob,
I attempted to reference existing properties from a common binding [1] as per
your feedback in [2], however 'make DT_CHECKER_FLAGS=-m dt_binding_check' fails
with the message 'Vendor specific properties must have a type and description
unless they have a defined, common suffix.'
This seems related to the discussion in [3], where you warned that the tooling
cannot yet deduce that vendor-specific properties have already been typed in an
externally $ref'd binding. The only other example of a common vendor schema is
[4], but in that case the common properties are defined under arbitraily named
pinmux config nodes. As such, they are part of 'additionalProperties' instead of
'properties' and hence exempt from this particular validation.
Please let me know if I am mistaken (surprise!), in which case I will continue
on this path and send a v3. Otherwise, I would like to suggest that the review
moves forward under the premise that I will happily consolidate these bindings
once the tooling supports this idea.
Kind regards,
Jeff LaBundy
[1] https://github.com/jlabundy/linux/tree/azoteq-common (WIP)
[2] https://patchwork.kernel.org/comment/25003573/
[3] https://patchwork.kernel.org/comment/23867857/
[4] Documentation/devicetree/bindings/pinctrl/nvidia,tegra30-pinmux.yaml
.../input/touchscreen/azoteq,iqs7211.yaml | 769 ++++++++++++++++++
1 file changed, 769 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml
diff --git a/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml b/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml
new file mode 100644
index 000000000000..8cf371b99f19
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml
@@ -0,0 +1,769 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/azoteq,iqs7211.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller
+
+maintainers:
+ - Jeff LaBundy <jeff@labundy.com>
+
+description: |
+ The Azoteq IQS7210A, IQS7211A and IQS7211E trackpad and touchscreen control-
+ lers employ projected-capacitance sensing and can track two contacts.
+
+ Link to datasheets: https://www.azoteq.com/
+
+properties:
+ compatible:
+ enum:
+ - azoteq,iqs7210a
+ - azoteq,iqs7211a
+ - azoteq,iqs7211e
+
+ reg:
+ maxItems: 1
+
+ irq-gpios:
+ maxItems: 1
+ description:
+ Specifies the GPIO connected to the device's active-low RDY output. The
+ pin doubles as the IQS7211E's active-low MCLR input, in which case this
+ GPIO must be configured as open-drain.
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ Specifies the GPIO connected to the device's active-low MCLR input. The
+ device is temporarily held in hardware reset prior to initialization if
+ this property is present.
+
+ azoteq,forced-comms:
+ type: boolean
+ description:
+ Enables forced communication; to be used with host adapters that cannot
+ tolerate clock stretching.
+
+ azoteq,forced-comms-default:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description:
+ Indicates if the device's OTP memory enables (1) or disables (0) forced
+ communication by default. Specifying this property can expedite startup
+ time if the default value is known.
+
+ If this property is not specified, communication is not initiated until
+ the device asserts its RDY pin shortly after exiting hardware reset. At
+ that point, forced communication is either enabled or disabled based on
+ the presence or absence of the 'azoteq,forced-comms' property.
+
+ azoteq,rate-active-ms:
+ minimum: 0
+ maximum: 65535
+ description: Specifies the report rate (in ms) during active mode.
+
+ azoteq,rate-touch-ms:
+ minimum: 0
+ maximum: 65535
+ description: Specifies the report rate (in ms) during idle-touch mode.
+
+ azoteq,rate-idle-ms:
+ minimum: 0
+ maximum: 65535
+ description: Specifies the report rate (in ms) during idle mode.
+
+ azoteq,rate-lp1-ms:
+ minimum: 0
+ maximum: 65535
+ description: Specifies the report rate (in ms) during low-power mode 1.
+
+ azoteq,rate-lp2-ms:
+ minimum: 0
+ maximum: 65535
+ description: Specifies the report rate (in ms) during low-power mode 2.
+
+ azoteq,timeout-active-ms:
+ multipleOf: 1000
+ minimum: 0
+ maximum: 65535000
+ description:
+ Specifies the length of time (in ms) to wait for an event before moving
+ from active mode to idle or idle-touch modes.
+
+ azoteq,timeout-touch-ms:
+ multipleOf: 1000
+ minimum: 0
+ maximum: 65535000
+ description:
+ Specifies the length of time (in ms) to wait for an event before moving
+ from idle-touch mode to idle mode.
+
+ azoteq,timeout-idle-ms:
+ multipleOf: 1000
+ minimum: 0
+ maximum: 65535000
+ description:
+ Specifies the length of time (in ms) to wait for an event before moving
+ from idle mode to low-power mode 1.
+
+ azoteq,timeout-lp1-ms:
+ multipleOf: 1000
+ minimum: 0
+ maximum: 65535000
+ description:
+ Specifies the length of time (in ms) to wait for an event before moving
+ from low-power mode 1 to low-power mode 2.
+
+ azoteq,timeout-lp2-ms:
+ multipleOf: 1000
+ minimum: 0
+ maximum: 60000
+ description:
+ Specifies the rate (in ms) at which the trackpad reference values
+ are updated during low-power modes 1 and 2.
+
+ azoteq,timeout-ati-ms:
+ multipleOf: 1000
+ minimum: 0
+ maximum: 60000
+ description:
+ Specifies the delay (in ms) before the automatic tuning implementation
+ (ATI) is retried in the event it fails to complete.
+
+ azoteq,timeout-comms-ms:
+ minimum: 0
+ maximum: 65535
+ description:
+ Specifies the delay (in ms) before a communication window is closed.
+
+ azoteq,timeout-press-ms:
+ multipleOf: 1000
+ minimum: 0
+ maximum: 60000
+ description:
+ Specifies the length of time (in ms) to wait before automatically
+ releasing a press event. Specify zero to allow the press state to
+ persist indefinitely.
+
+ azoteq,fosc-freq:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description: |
+ Specifies the device's core clock frequency as follows:
+ 0: 14 MHz
+ 1: 18 MHz
+
+ azoteq,fosc-trim:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 15
+ description: Specifies the device's core clock frequency trim.
+
+ azoteq,num-contacts:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 2
+ default: 0
+ description: Specifies the number of contacts reported by the device.
+
+ azoteq,contact-split:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the contact (finger) split factor.
+
+ azoteq,trim-x:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the horizontal trim width.
+
+ azoteq,trim-y:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the vertical trim height.
+
+ trackpad:
+ type: object
+ description: Represents all channels associated with the trackpad.
+
+ properties:
+ azoteq,rx-enable:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 8
+ items:
+ minimum: 0
+ maximum: 7
+ description:
+ Specifies the order of the CRx pin(s) associated with the trackpad.
+
+ azoteq,tx-enable:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 12
+ items:
+ minimum: 0
+ maximum: 11
+ description:
+ Specifies the order of the CTx pin(s) associated with the trackpad.
+
+ azoteq,channel-select:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 36
+ items:
+ minimum: 0
+ maximum: 255
+ description: |
+ Specifies the channels mapped to each cycle in the following order:
+ Cycle 0, slot 0
+ Cycle 0, slot 1
+ Cycle 1, slot 0
+ Cycle 1, slot 1
+ ...and so on. Specify 255 to disable a given slot.
+
+ azoteq,ati-frac-div-fine:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the trackpad's ATI fine fractional divider.
+
+ azoteq,ati-frac-mult-coarse:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 15
+ description: Specifies the trackpad's ATI coarse fractional multiplier.
+
+ azoteq,ati-frac-div-coarse:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the trackpad's ATI coarse fractional divider.
+
+ azoteq,ati-comp-div:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the trackpad's ATI compensation divider.
+
+ azoteq,ati-target:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 65535
+ description: Specifies the trackpad's ATI target.
+
+ azoteq,touch-enter:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the trackpad's touch entrance factor.
+
+ azoteq,touch-exit:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the trackpad's touch exit factor.
+
+ azoteq,thresh:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the trackpad's stationary touch threshold.
+
+ azoteq,conv-period:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the trackpad's conversion period.
+
+ azoteq,conv-frac:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the trackpad's conversion frequency fraction.
+
+ patternProperties:
+ "^event-(tap(-double|-triple)?|hold|palm|swipe-(x|y)-(pos|neg)(-hold)?)$":
+ type: object
+ $ref: ../input.yaml#
+ description:
+ Represents a gesture event reported by the trackpad. In the case of
+ axial gestures, the duration or distance specified in one direction
+ applies to both directions along the same axis.
+
+ properties:
+ linux,code: true
+
+ azoteq,gesture-max-ms:
+ minimum: 0
+ maximum: 65535
+ description: Specifies the maximum duration of tap/swipe gestures.
+
+ azoteq,gesture-mid-ms:
+ minimum: 0
+ maximum: 65535
+ description:
+ Specifies the maximum duration between subsequent tap gestures
+ (IQS7211E only).
+
+ azoteq,gesture-min-ms:
+ minimum: 0
+ maximum: 65535
+ description: Specifies the minimum duration of hold gestures.
+
+ azoteq,gesture-dist:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 65535
+ description:
+ Specifies the minimum (swipe) or maximum (tap and hold) distance
+ a finger may travel to be considered a gesture.
+
+ azoteq,gesture-dist-rep:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 65535
+ description:
+ Specifies the minimum distance a finger must travel to elicit a
+ repeated swipe gesture (IQS7211E only).
+
+ azoteq,gesture-angle:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 75
+ description:
+ Specifies the maximum angle (in degrees) a finger may travel to
+ be considered a swipe gesture.
+
+ azoteq,thresh:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 42
+ description: Specifies the palm gesture threshold (IQS7211E only).
+
+ additionalProperties: false
+
+ dependencies:
+ azoteq,rx-enable: ["azoteq,tx-enable"]
+ azoteq,tx-enable: ["azoteq,rx-enable"]
+ azoteq,channel-select: ["azoteq,rx-enable"]
+
+ additionalProperties: false
+
+ alp:
+ type: object
+ $ref: ../input.yaml#
+ description: Represents the alternate low-power channel (ALP).
+
+ properties:
+ azoteq,rx-enable:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 8
+ items:
+ minimum: 0
+ maximum: 7
+ description:
+ Specifies the CRx pin(s) associated with the ALP in no particular
+ order.
+
+ azoteq,tx-enable:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 12
+ items:
+ minimum: 0
+ maximum: 11
+ description:
+ Specifies the CTx pin(s) associated with the ALP in no particular
+ order.
+
+ azoteq,ati-frac-div-fine:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the ALP's ATI fine fractional divider.
+
+ azoteq,ati-frac-mult-coarse:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 15
+ description: Specifies the ALP's ATI coarse fractional multiplier.
+
+ azoteq,ati-frac-div-coarse:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the ALP's ATI coarse fractional divider.
+
+ azoteq,ati-comp-div:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the ALP's ATI compensation divider.
+
+ azoteq,ati-target:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 65535
+ description: Specifies the ALP's ATI target.
+
+ azoteq,ati-base:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ multipleOf: 8
+ minimum: 0
+ maximum: 255
+ description: Specifies the ALP's ATI base.
+
+ azoteq,ati-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description: |
+ Specifies the ALP's ATI mode as follows:
+ 0: Partial
+ 1: Full
+
+ azoteq,sense-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description: |
+ Specifies the ALP's sensing mode as follows:
+ 0: Self capacitive
+ 1: Mutual capacitive
+
+ azoteq,debounce-enter:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the ALP's debounce entrance factor.
+
+ azoteq,debounce-exit:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the ALP's debounce exit factor.
+
+ azoteq,thresh:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 65535
+ description: Specifies the ALP's proximity or touch threshold.
+
+ azoteq,conv-period:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the ALP's conversion period.
+
+ azoteq,conv-frac:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the ALP's conversion frequency fraction.
+
+ linux,code: true
+
+ additionalProperties: false
+
+ button:
+ type: object
+ description: Represents the inductive or capacitive button.
+
+ properties:
+ azoteq,ati-frac-div-fine:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the button's ATI fine fractional divider.
+
+ azoteq,ati-frac-mult-coarse:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 15
+ description: Specifies the button's ATI coarse fractional multiplier.
+
+ azoteq,ati-frac-div-coarse:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the button's ATI coarse fractional divider.
+
+ azoteq,ati-comp-div:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 31
+ description: Specifies the button's ATI compensation divider.
+
+ azoteq,ati-target:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 65535
+ description: Specifies the button's ATI target.
+
+ azoteq,ati-base:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ multipleOf: 8
+ minimum: 0
+ maximum: 255
+ description: Specifies the button's ATI base.
+
+ azoteq,ati-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description: |
+ Specifies the button's ATI mode as follows:
+ 0: Partial
+ 1: Full
+
+ azoteq,sense-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1, 2]
+ description: |
+ Specifies the button's sensing mode as follows:
+ 0: Self capacitive
+ 1: Mutual capacitive
+ 2: Inductive
+
+ azoteq,touch-enter:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the button's touch entrance factor.
+
+ azoteq,touch-exit:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the button's touch exit factor.
+
+ azoteq,debounce-enter:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the button's debounce entrance factor.
+
+ azoteq,debounce-exit:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the button's debounce exit factor.
+
+ azoteq,thresh:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 65535
+ description: Specifies the button's proximity threshold.
+
+ azoteq,conv-period:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the button's conversion period.
+
+ azoteq,conv-frac:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+ description: Specifies the button's conversion frequency fraction.
+
+ patternProperties:
+ "^event-(prox|touch)$":
+ type: object
+ $ref: ../input.yaml#
+ description:
+ Represents a proximity or touch event reported by the button.
+
+ properties:
+ linux,code: true
+
+ additionalProperties: false
+
+ additionalProperties: false
+
+ wakeup-source: true
+
+ touchscreen-size-x: true
+ touchscreen-size-y: true
+ touchscreen-inverted-x: true
+ touchscreen-inverted-y: true
+ touchscreen-swapped-x-y: true
+
+dependencies:
+ touchscreen-size-x: ["azoteq,num-contacts"]
+ touchscreen-size-y: ["azoteq,num-contacts"]
+ touchscreen-inverted-x: ["azoteq,num-contacts"]
+ touchscreen-inverted-y: ["azoteq,num-contacts"]
+ touchscreen-swapped-x-y: ["azoteq,num-contacts"]
+
+required:
+ - compatible
+ - reg
+ - irq-gpios
+
+additionalProperties: false
+
+allOf:
+ - $ref: touchscreen.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: azoteq,iqs7210a
+
+ then:
+ properties:
+ alp:
+ properties:
+ azoteq,rx-enable:
+ maxItems: 4
+ items:
+ minimum: 4
+
+ else:
+ properties:
+ azoteq,timeout-press-ms: false
+
+ alp:
+ properties:
+ azoteq,ati-mode: false
+
+ button: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: azoteq,iqs7211e
+
+ then:
+ properties:
+ reset-gpios: false
+
+ trackpad:
+ properties:
+ azoteq,tx-enable:
+ maxItems: 13
+ items:
+ maximum: 12
+
+ alp:
+ properties:
+ azoteq,tx-enable:
+ maxItems: 13
+ items:
+ maximum: 12
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/input/input.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touch@56 {
+ compatible = "azoteq,iqs7210a";
+ reg = <0x56>;
+ irq-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
+ reset-gpios = <&gpio 17 (GPIO_ACTIVE_LOW |
+ GPIO_PUSH_PULL)>;
+ azoteq,num-contacts = <2>;
+
+ trackpad {
+ azoteq,rx-enable = <6>, <5>, <4>, <3>, <2>;
+ azoteq,tx-enable = <1>, <7>, <8>, <9>, <10>;
+ };
+
+ button {
+ azoteq,sense-mode = <2>;
+ azoteq,touch-enter = <40>;
+ azoteq,touch-exit = <36>;
+
+ event-touch {
+ linux,code = <KEY_HOME>;
+ };
+ };
+
+ alp {
+ azoteq,sense-mode = <1>;
+ linux,code = <KEY_POWER>;
+ };
+ };
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/input/input.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touch@56 {
+ compatible = "azoteq,iqs7211e";
+ reg = <0x56>;
+ irq-gpios = <&gpio 4 (GPIO_ACTIVE_LOW |
+ GPIO_OPEN_DRAIN)>;
+
+ trackpad {
+ event-tap {
+ linux,code = <KEY_PLAYPAUSE>;
+ };
+
+ event-tap-double {
+ linux,code = <KEY_SHUFFLE>;
+ };
+
+ event-tap-triple {
+ linux,code = <KEY_AGAIN>;
+ };
+
+ event-hold {
+ linux,code = <KEY_STOP>;
+ };
+
+ event-palm {
+ linux,code = <KEY_EXIT>;
+ };
+
+ event-swipe-x-pos {
+ linux,code = <KEY_REWIND>;
+ };
+
+ event-swipe-x-pos-hold {
+ linux,code = <KEY_PREVIOUS>;
+ };
+
+ event-swipe-x-neg {
+ linux,code = <KEY_FASTFORWARD>;
+ };
+
+ event-swipe-x-neg-hold {
+ linux,code = <KEY_NEXT>;
+ };
+
+ event-swipe-y-pos {
+ linux,code = <KEY_VOLUMEUP>;
+ };
+
+ event-swipe-y-pos-hold {
+ linux,code = <KEY_MUTE>;
+ };
+
+ event-swipe-y-neg {
+ linux,code = <KEY_VOLUMEDOWN>;
+ };
+
+ event-swipe-y-neg-hold {
+ linux,code = <KEY_MUTE>;
+ };
+ };
+ };
+ };
+
+...
--
2.34.1
^ permalink raw reply related
* [PATCH v2 2/2] Input: add support for Azoteq IQS7210A/7211A/E
From: Jeff LaBundy @ 2023-05-30 0:34 UTC (permalink / raw)
To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, jeff
In-Reply-To: <ZHVD/9OgRTAwBhqx@nixie71>
Add support for the Azoteq IQS7210A/7211A/E family of trackpad/
touchscreen controllers.
Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
- Replaced multiple calls to usleep_range() with iqs7211_irq_wait() and
increased the delay to accommodate typical RC rise time
- Updated iqs7211_irq_poll() to call readx_poll_timeout() instead of open-
coding the same operation
- Updated iqs7211_hard_reset() to assert the shared RDY/MCLR pin for a
sufficiently long delay to ensure the pin is asserted outside of a
communication window instead of polling for a narrow pulse width
- Trimmed the force communication command in iqs7211_force_comms() as per the
vendor's recommendation
- Added an instance of iqs7211_irq_wait() to iqs7211_force_comms() to prevent
premature closure of early communication windows
- Replaced the logic that parsed the old 'azoteq,default-comms' property with
logic that parses the new 'azoteq,forced-comms-default' property and stored
this value for use by the interrupt handler
- Checked the return values of multiple calls to fwnode_property_count_u32()
against -EINVAL instead of additionally calling fwnode_property_present()
- Replaced various functions with iqs7211_parse_reg_grp(), iqs7211_parse_extra()
and iqs7211_parse_event() to reduce the number of calls to fwnode_handle_put()
- Added a look-up table to support interpreting the 'azoteq,gesture-angle'
property in units of degrees
- Optimized the logic responsible for requesting shared vs. separate interrupt
and reset GPIOs
- Added the recently introduced DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
macros
- Replaced the probe_new() callback with the "new" probe() callback
drivers/input/touchscreen/Kconfig | 10 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/iqs7211.c | 2569 +++++++++++++++++++++++++++
3 files changed, 2580 insertions(+)
create mode 100644 drivers/input/touchscreen/iqs7211.c
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index c2cbd332af1d..3f493e3260b4 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1365,6 +1365,16 @@ config TOUCHSCREEN_IQS5XX
To compile this driver as a module, choose M here: the
module will be called iqs5xx.
+config TOUCHSCREEN_IQS7211
+ tristate "Azoteq IQS7210A/7211A/E trackpad/touchscreen controller"
+ depends on I2C
+ help
+ Say Y to enable support for the Azoteq IQS7210A/7211A/E
+ family of trackpad/touchscreen controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iqs7211.
+
config TOUCHSCREEN_ZINITIX
tristate "Zinitix touchscreen support"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 159cd5136fdb..62bd24f3ac8e 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -115,5 +115,6 @@ obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o
obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o
+obj-$(CONFIG_TOUCHSCREEN_IQS7211) += iqs7211.o
obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o
obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o
diff --git a/drivers/input/touchscreen/iqs7211.c b/drivers/input/touchscreen/iqs7211.c
new file mode 100644
index 000000000000..f60316d37f45
--- /dev/null
+++ b/drivers/input/touchscreen/iqs7211.c
@@ -0,0 +1,2569 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller
+ *
+ * Copyright (C) 2023 Jeff LaBundy <jeff@labundy.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#define IQS7211_PROD_NUM 0x00
+
+#define IQS7211_EVENT_MASK_ALL GENMASK(14, 8)
+#define IQS7211_EVENT_MASK_ALP BIT(13)
+#define IQS7211_EVENT_MASK_BTN BIT(12)
+#define IQS7211_EVENT_MASK_ATI BIT(11)
+#define IQS7211_EVENT_MASK_MOVE BIT(10)
+#define IQS7211_EVENT_MASK_GSTR BIT(9)
+#define IQS7211_EVENT_MODE BIT(8)
+
+#define IQS7211_COMMS_ERROR 0xEEEE
+#define IQS7211_COMMS_RETRY_MS 50
+#define IQS7211_COMMS_SLEEP_US 100
+#define IQS7211_COMMS_TIMEOUT_US (100 * USEC_PER_MSEC)
+#define IQS7211_RESET_TIMEOUT_MS 150
+#define IQS7211_START_TIMEOUT_US (1 * USEC_PER_SEC)
+
+#define IQS7211_NUM_RETRIES 5
+#define IQS7211_NUM_CRX 8
+#define IQS7211_MAX_CTX 13
+
+#define IQS7211_MAX_CONTACTS 2
+#define IQS7211_MAX_CYCLES 21
+
+/*
+ * The following delay is used during instances that must wait for the open-
+ * drain RDY pin to settle. Its value is calculated as 5*R*C, where R and C
+ * represent typical datasheet values of 4.7k and 100 nF, respectively.
+ */
+#define iqs7211_irq_wait() usleep_range(2500, 2600)
+
+enum iqs7211_dev_id {
+ IQS7210A,
+ IQS7211A,
+ IQS7211E,
+};
+
+enum iqs7211_comms_mode {
+ IQS7211_COMMS_MODE_WAIT,
+ IQS7211_COMMS_MODE_FREE,
+ IQS7211_COMMS_MODE_FORCE,
+};
+
+struct iqs7211_reg_field_desc {
+ struct list_head list;
+ u8 addr;
+ u16 mask;
+ u16 val;
+};
+
+enum iqs7211_reg_key_id {
+ IQS7211_REG_KEY_NONE,
+ IQS7211_REG_KEY_PROX,
+ IQS7211_REG_KEY_TOUCH,
+ IQS7211_REG_KEY_TAP,
+ IQS7211_REG_KEY_HOLD,
+ IQS7211_REG_KEY_PALM,
+ IQS7211_REG_KEY_AXIAL_X,
+ IQS7211_REG_KEY_AXIAL_Y,
+ IQS7211_REG_KEY_RESERVED
+};
+
+enum iqs7211_reg_grp_id {
+ IQS7211_REG_GRP_TP,
+ IQS7211_REG_GRP_BTN,
+ IQS7211_REG_GRP_ALP,
+ IQS7211_REG_GRP_SYS,
+ IQS7211_NUM_REG_GRPS
+};
+
+static const char * const iqs7211_reg_grp_names[IQS7211_NUM_REG_GRPS] = {
+ [IQS7211_REG_GRP_TP] = "trackpad",
+ [IQS7211_REG_GRP_BTN] = "button",
+ [IQS7211_REG_GRP_ALP] = "alp",
+};
+
+static const u16 iqs7211_reg_grp_masks[IQS7211_NUM_REG_GRPS] = {
+ [IQS7211_REG_GRP_TP] = IQS7211_EVENT_MASK_GSTR,
+ [IQS7211_REG_GRP_BTN] = IQS7211_EVENT_MASK_BTN,
+ [IQS7211_REG_GRP_ALP] = IQS7211_EVENT_MASK_ALP,
+};
+
+struct iqs7211_event_desc {
+ const char *name;
+ u16 mask;
+ u16 enable;
+ enum iqs7211_reg_grp_id reg_grp;
+ enum iqs7211_reg_key_id reg_key;
+};
+
+static const struct iqs7211_event_desc iqs7210a_kp_events[] = {
+ {
+ .mask = BIT(10),
+ .enable = BIT(13) | BIT(12),
+ .reg_grp = IQS7211_REG_GRP_ALP,
+ },
+ {
+ .name = "event-prox",
+ .mask = BIT(2),
+ .enable = BIT(5) | BIT(4),
+ .reg_grp = IQS7211_REG_GRP_BTN,
+ .reg_key = IQS7211_REG_KEY_PROX,
+ },
+ {
+ .name = "event-touch",
+ .mask = BIT(3),
+ .enable = BIT(5) | BIT(4),
+ .reg_grp = IQS7211_REG_GRP_BTN,
+ .reg_key = IQS7211_REG_KEY_TOUCH,
+ },
+ {
+ .name = "event-tap",
+ .mask = BIT(0),
+ .enable = BIT(0),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_TAP,
+ },
+ {
+ .name = "event-hold",
+ .mask = BIT(1),
+ .enable = BIT(1),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ },
+ {
+ .name = "event-swipe-x-neg",
+ .mask = BIT(2),
+ .enable = BIT(2),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ },
+ {
+ .name = "event-swipe-x-pos",
+ .mask = BIT(3),
+ .enable = BIT(3),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ },
+ {
+ .name = "event-swipe-y-pos",
+ .mask = BIT(4),
+ .enable = BIT(4),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ },
+ {
+ .name = "event-swipe-y-neg",
+ .mask = BIT(5),
+ .enable = BIT(5),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ },
+};
+
+static const struct iqs7211_event_desc iqs7211a_kp_events[] = {
+ {
+ .mask = BIT(14),
+ .reg_grp = IQS7211_REG_GRP_ALP,
+ },
+ {
+ .name = "event-tap",
+ .mask = BIT(0),
+ .enable = BIT(0),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_TAP,
+ },
+ {
+ .name = "event-hold",
+ .mask = BIT(1),
+ .enable = BIT(1),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ },
+ {
+ .name = "event-swipe-x-neg",
+ .mask = BIT(2),
+ .enable = BIT(2),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ },
+ {
+ .name = "event-swipe-x-pos",
+ .mask = BIT(3),
+ .enable = BIT(3),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ },
+ {
+ .name = "event-swipe-y-pos",
+ .mask = BIT(4),
+ .enable = BIT(4),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ },
+ {
+ .name = "event-swipe-y-neg",
+ .mask = BIT(5),
+ .enable = BIT(5),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ },
+};
+
+static const struct iqs7211_event_desc iqs7211e_kp_events[] = {
+ {
+ .mask = BIT(14),
+ .reg_grp = IQS7211_REG_GRP_ALP,
+ },
+ {
+ .name = "event-tap",
+ .mask = BIT(0),
+ .enable = BIT(0),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_TAP,
+ },
+ {
+ .name = "event-tap-double",
+ .mask = BIT(1),
+ .enable = BIT(1),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_TAP,
+ },
+ {
+ .name = "event-tap-triple",
+ .mask = BIT(2),
+ .enable = BIT(2),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_TAP,
+ },
+ {
+ .name = "event-hold",
+ .mask = BIT(3),
+ .enable = BIT(3),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ },
+ {
+ .name = "event-palm",
+ .mask = BIT(4),
+ .enable = BIT(4),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_PALM,
+ },
+ {
+ .name = "event-swipe-x-pos",
+ .mask = BIT(8),
+ .enable = BIT(8),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ },
+ {
+ .name = "event-swipe-x-neg",
+ .mask = BIT(9),
+ .enable = BIT(9),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ },
+ {
+ .name = "event-swipe-y-pos",
+ .mask = BIT(10),
+ .enable = BIT(10),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ },
+ {
+ .name = "event-swipe-y-neg",
+ .mask = BIT(11),
+ .enable = BIT(11),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ },
+ {
+ .name = "event-swipe-x-pos-hold",
+ .mask = BIT(12),
+ .enable = BIT(12),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ },
+ {
+ .name = "event-swipe-x-neg-hold",
+ .mask = BIT(13),
+ .enable = BIT(13),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ },
+ {
+ .name = "event-swipe-y-pos-hold",
+ .mask = BIT(14),
+ .enable = BIT(14),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ },
+ {
+ .name = "event-swipe-y-neg-hold",
+ .mask = BIT(15),
+ .enable = BIT(15),
+ .reg_grp = IQS7211_REG_GRP_TP,
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ },
+};
+
+struct iqs7211_dev_desc {
+ const char *tp_name;
+ const char *kp_name;
+ u16 prod_num;
+ u16 show_reset;
+ u16 ati_error[IQS7211_NUM_REG_GRPS];
+ u16 ati_start[IQS7211_NUM_REG_GRPS];
+ u16 suspend;
+ u16 ack_reset;
+ u16 comms_end;
+ u16 comms_req;
+ int charge_shift;
+ int info_offs;
+ int gesture_offs;
+ int contact_offs;
+ u8 sys_stat;
+ u8 sys_ctrl;
+ u8 alp_config;
+ u8 tp_config;
+ u8 exp_file;
+ u8 kp_enable[IQS7211_NUM_REG_GRPS];
+ u8 gesture_angle;
+ u8 rx_tx_map;
+ u8 cycle_alloc[2];
+ u8 cycle_limit[2];
+ const struct iqs7211_event_desc *kp_events;
+ int num_kp_events;
+ int min_crx_alp;
+ int num_ctx;
+};
+
+static const struct iqs7211_dev_desc iqs7211_devs[] = {
+ [IQS7210A] = {
+ .tp_name = "iqs7210a_trackpad",
+ .kp_name = "iqs7210a_keys",
+ .prod_num = 944,
+ .show_reset = BIT(15),
+ .ati_error = {
+ [IQS7211_REG_GRP_TP] = BIT(12),
+ [IQS7211_REG_GRP_BTN] = BIT(0),
+ [IQS7211_REG_GRP_ALP] = BIT(8),
+ },
+ .ati_start = {
+ [IQS7211_REG_GRP_TP] = BIT(13),
+ [IQS7211_REG_GRP_BTN] = BIT(1),
+ [IQS7211_REG_GRP_ALP] = BIT(9),
+ },
+ .suspend = BIT(11),
+ .ack_reset = BIT(7),
+ .comms_end = BIT(2),
+ .comms_req = BIT(1),
+ .charge_shift = 4,
+ .info_offs = 0,
+ .gesture_offs = 1,
+ .contact_offs = 4,
+ .sys_stat = 0x0A,
+ .sys_ctrl = 0x35,
+ .alp_config = 0x39,
+ .tp_config = 0x4E,
+ .exp_file = 0x57,
+ .kp_enable = {
+ [IQS7211_REG_GRP_TP] = 0x58,
+ [IQS7211_REG_GRP_BTN] = 0x37,
+ [IQS7211_REG_GRP_ALP] = 0x37,
+ },
+ .gesture_angle = 0x5F,
+ .rx_tx_map = 0x60,
+ .cycle_alloc = { 0x66, 0x75, },
+ .cycle_limit = { 10, 6, },
+ .kp_events = iqs7210a_kp_events,
+ .num_kp_events = ARRAY_SIZE(iqs7210a_kp_events),
+ .min_crx_alp = 4,
+ .num_ctx = IQS7211_MAX_CTX - 1,
+ },
+ [IQS7211A] = {
+ .tp_name = "iqs7211a_trackpad",
+ .kp_name = "iqs7211a_keys",
+ .prod_num = 763,
+ .show_reset = BIT(7),
+ .ati_error = {
+ [IQS7211_REG_GRP_TP] = BIT(3),
+ [IQS7211_REG_GRP_ALP] = BIT(5),
+ },
+ .ati_start = {
+ [IQS7211_REG_GRP_TP] = BIT(5),
+ [IQS7211_REG_GRP_ALP] = BIT(6),
+ },
+ .ack_reset = BIT(7),
+ .comms_req = BIT(4),
+ .charge_shift = 0,
+ .info_offs = 0,
+ .gesture_offs = 1,
+ .contact_offs = 4,
+ .sys_stat = 0x10,
+ .sys_ctrl = 0x50,
+ .tp_config = 0x60,
+ .alp_config = 0x72,
+ .exp_file = 0x74,
+ .kp_enable = {
+ [IQS7211_REG_GRP_TP] = 0x80,
+ },
+ .gesture_angle = 0x87,
+ .rx_tx_map = 0x90,
+ .cycle_alloc = { 0xA0, 0xB0, },
+ .cycle_limit = { 10, 8, },
+ .kp_events = iqs7211a_kp_events,
+ .num_kp_events = ARRAY_SIZE(iqs7211a_kp_events),
+ .num_ctx = IQS7211_MAX_CTX - 1,
+ },
+ [IQS7211E] = {
+ .tp_name = "iqs7211e_trackpad",
+ .kp_name = "iqs7211e_keys",
+ .prod_num = 1112,
+ .show_reset = BIT(7),
+ .ati_error = {
+ [IQS7211_REG_GRP_TP] = BIT(3),
+ [IQS7211_REG_GRP_ALP] = BIT(5),
+ },
+ .ati_start = {
+ [IQS7211_REG_GRP_TP] = BIT(5),
+ [IQS7211_REG_GRP_ALP] = BIT(6),
+ },
+ .suspend = BIT(11),
+ .ack_reset = BIT(7),
+ .comms_end = BIT(6),
+ .comms_req = BIT(4),
+ .charge_shift = 0,
+ .info_offs = 1,
+ .gesture_offs = 0,
+ .contact_offs = 2,
+ .sys_stat = 0x0E,
+ .sys_ctrl = 0x33,
+ .tp_config = 0x41,
+ .alp_config = 0x36,
+ .exp_file = 0x4A,
+ .kp_enable = {
+ [IQS7211_REG_GRP_TP] = 0x4B,
+ },
+ .gesture_angle = 0x55,
+ .rx_tx_map = 0x56,
+ .cycle_alloc = { 0x5D, 0x6C, },
+ .cycle_limit = { 10, 11, },
+ .kp_events = iqs7211e_kp_events,
+ .num_kp_events = ARRAY_SIZE(iqs7211e_kp_events),
+ .num_ctx = IQS7211_MAX_CTX,
+ },
+};
+
+struct iqs7211_prop_desc {
+ const char *name;
+ enum iqs7211_reg_key_id reg_key;
+ u8 reg_addr[IQS7211_NUM_REG_GRPS][ARRAY_SIZE(iqs7211_devs)];
+ int reg_shift;
+ int reg_width;
+ int val_pitch;
+ int val_min;
+ int val_max;
+ const char *label;
+};
+
+static const struct iqs7211_prop_desc iqs7211_props[] = {
+ {
+ .name = "azoteq,ati-frac-div-fine",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x1E,
+ [IQS7211A] = 0x30,
+ [IQS7211E] = 0x21,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x22,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x23,
+ [IQS7211A] = 0x36,
+ [IQS7211E] = 0x25,
+ },
+ },
+ .reg_shift = 9,
+ .reg_width = 5,
+ .label = "ATI fine fractional divider",
+ },
+ {
+ .name = "azoteq,ati-frac-mult-coarse",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x1E,
+ [IQS7211A] = 0x30,
+ [IQS7211E] = 0x21,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x22,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x23,
+ [IQS7211A] = 0x36,
+ [IQS7211E] = 0x25,
+ },
+ },
+ .reg_shift = 5,
+ .reg_width = 4,
+ .label = "ATI coarse fractional multiplier",
+ },
+ {
+ .name = "azoteq,ati-frac-div-coarse",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x1E,
+ [IQS7211A] = 0x30,
+ [IQS7211E] = 0x21,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x22,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x23,
+ [IQS7211A] = 0x36,
+ [IQS7211E] = 0x25,
+ },
+ },
+ .reg_shift = 0,
+ .reg_width = 5,
+ .label = "ATI coarse fractional divider",
+ },
+ {
+ .name = "azoteq,ati-comp-div",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x1F,
+ [IQS7211E] = 0x22,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x24,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7211E] = 0x26,
+ },
+ },
+ .reg_shift = 0,
+ .reg_width = 8,
+ .val_max = 31,
+ .label = "ATI compensation divider",
+ },
+ {
+ .name = "azoteq,ati-comp-div",
+ .reg_addr = {
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x24,
+ },
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_max = 31,
+ .label = "ATI compensation divider",
+ },
+ {
+ .name = "azoteq,ati-comp-div",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7211A] = 0x31,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7211A] = 0x37,
+ },
+ },
+ .val_max = 31,
+ .label = "ATI compensation divider",
+ },
+ {
+ .name = "azoteq,ati-target",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x20,
+ [IQS7211A] = 0x32,
+ [IQS7211E] = 0x23,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x27,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x28,
+ [IQS7211A] = 0x38,
+ [IQS7211E] = 0x27,
+ },
+ },
+ .label = "ATI target",
+ },
+ {
+ .name = "azoteq,ati-base",
+ .reg_addr[IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x26,
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 8,
+ .label = "ATI base",
+ },
+ {
+ .name = "azoteq,ati-base",
+ .reg_addr[IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x26,
+ },
+ .reg_shift = 0,
+ .reg_width = 8,
+ .val_pitch = 8,
+ .label = "ATI base",
+ },
+ {
+ .name = "azoteq,rate-active-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x29,
+ [IQS7211A] = 0x40,
+ [IQS7211E] = 0x28,
+ },
+ .label = "active mode report rate",
+ },
+ {
+ .name = "azoteq,rate-touch-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x2A,
+ [IQS7211A] = 0x41,
+ [IQS7211E] = 0x29,
+ },
+ .label = "idle-touch mode report rate",
+ },
+ {
+ .name = "azoteq,rate-idle-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x2B,
+ [IQS7211A] = 0x42,
+ [IQS7211E] = 0x2A,
+ },
+ .label = "idle mode report rate",
+ },
+ {
+ .name = "azoteq,rate-lp1-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x2C,
+ [IQS7211A] = 0x43,
+ [IQS7211E] = 0x2B,
+ },
+ .label = "low-power mode 1 report rate",
+ },
+ {
+ .name = "azoteq,rate-lp2-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x2D,
+ [IQS7211A] = 0x44,
+ [IQS7211E] = 0x2C,
+ },
+ .label = "low-power mode 2 report rate",
+ },
+ {
+ .name = "azoteq,timeout-active-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x2E,
+ [IQS7211A] = 0x45,
+ [IQS7211E] = 0x2D,
+ },
+ .val_pitch = 1000,
+ .label = "active mode timeout",
+ },
+ {
+ .name = "azoteq,timeout-touch-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x2F,
+ [IQS7211A] = 0x46,
+ [IQS7211E] = 0x2E,
+ },
+ .val_pitch = 1000,
+ .label = "idle-touch mode timeout",
+ },
+ {
+ .name = "azoteq,timeout-idle-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x30,
+ [IQS7211A] = 0x47,
+ [IQS7211E] = 0x2F,
+ },
+ .val_pitch = 1000,
+ .label = "idle mode timeout",
+ },
+ {
+ .name = "azoteq,timeout-lp1-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x31,
+ [IQS7211A] = 0x48,
+ [IQS7211E] = 0x30,
+ },
+ .val_pitch = 1000,
+ .label = "low-power mode 1 timeout",
+ },
+ {
+ .name = "azoteq,timeout-lp2-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x32,
+ [IQS7211E] = 0x31,
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_pitch = 1000,
+ .val_max = 60000,
+ .label = "trackpad reference value update rate",
+ },
+ {
+ .name = "azoteq,timeout-lp2-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7211A] = 0x49,
+ },
+ .val_pitch = 1000,
+ .val_max = 60000,
+ .label = "trackpad reference value update rate",
+ },
+ {
+ .name = "azoteq,timeout-ati-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x32,
+ [IQS7211E] = 0x31,
+ },
+ .reg_width = 8,
+ .val_pitch = 1000,
+ .val_max = 60000,
+ .label = "ATI error timeout",
+ },
+ {
+ .name = "azoteq,timeout-ati-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7211A] = 0x35,
+ },
+ .val_pitch = 1000,
+ .val_max = 60000,
+ .label = "ATI error timeout",
+ },
+ {
+ .name = "azoteq,timeout-comms-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x33,
+ [IQS7211A] = 0x4A,
+ [IQS7211E] = 0x32,
+ },
+ .label = "communication timeout",
+ },
+ {
+ .name = "azoteq,timeout-press-ms",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x34,
+ },
+ .reg_width = 8,
+ .val_pitch = 1000,
+ .val_max = 60000,
+ .label = "press timeout",
+ },
+ {
+ .name = "azoteq,ati-mode",
+ .reg_addr[IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x37,
+ },
+ .reg_shift = 15,
+ .reg_width = 1,
+ .label = "ATI mode",
+ },
+ {
+ .name = "azoteq,ati-mode",
+ .reg_addr[IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x37,
+ },
+ .reg_shift = 7,
+ .reg_width = 1,
+ .label = "ATI mode",
+ },
+ {
+ .name = "azoteq,sense-mode",
+ .reg_addr[IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x37,
+ [IQS7211A] = 0x72,
+ [IQS7211E] = 0x36,
+ },
+ .reg_shift = 8,
+ .reg_width = 1,
+ .label = "sensing mode",
+ },
+ {
+ .name = "azoteq,sense-mode",
+ .reg_addr[IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x37,
+ },
+ .reg_shift = 0,
+ .reg_width = 2,
+ .val_max = 2,
+ .label = "sensing mode",
+ },
+ {
+ .name = "azoteq,fosc-freq",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x38,
+ [IQS7211A] = 0x52,
+ [IQS7211E] = 0x35,
+ },
+ .reg_shift = 4,
+ .reg_width = 1,
+ .label = "core clock frequency selection",
+ },
+ {
+ .name = "azoteq,fosc-trim",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x38,
+ [IQS7211A] = 0x52,
+ [IQS7211E] = 0x35,
+ },
+ .reg_shift = 0,
+ .reg_width = 4,
+ .label = "core clock frequency trim",
+ },
+ {
+ .name = "azoteq,touch-exit",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x3B,
+ [IQS7211A] = 0x53,
+ [IQS7211E] = 0x38,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x3E,
+ },
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "touch exit factor",
+ },
+ {
+ .name = "azoteq,touch-enter",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x3B,
+ [IQS7211A] = 0x53,
+ [IQS7211E] = 0x38,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x3E,
+ },
+ },
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "touch entrance factor",
+ },
+ {
+ .name = "azoteq,thresh",
+ .reg_addr = {
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x3C,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x3D,
+ [IQS7211A] = 0x54,
+ [IQS7211E] = 0x39,
+ },
+ },
+ .label = "threshold",
+ },
+ {
+ .name = "azoteq,debounce-exit",
+ .reg_addr = {
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x3F,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x40,
+ [IQS7211A] = 0x56,
+ [IQS7211E] = 0x3A,
+ },
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "debounce exit factor",
+ },
+ {
+ .name = "azoteq,debounce-enter",
+ .reg_addr = {
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x3F,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x40,
+ [IQS7211A] = 0x56,
+ [IQS7211E] = 0x3A,
+ },
+ },
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "debounce entrance factor",
+ },
+ {
+ .name = "azoteq,conv-frac",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x48,
+ [IQS7211A] = 0x58,
+ [IQS7211E] = 0x3D,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x49,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x4A,
+ [IQS7211A] = 0x59,
+ [IQS7211E] = 0x3E,
+ },
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "conversion frequency fractional divider",
+ },
+ {
+ .name = "azoteq,conv-period",
+ .reg_addr = {
+ [IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x48,
+ [IQS7211A] = 0x58,
+ [IQS7211E] = 0x3D,
+ },
+ [IQS7211_REG_GRP_BTN] = {
+ [IQS7210A] = 0x49,
+ },
+ [IQS7211_REG_GRP_ALP] = {
+ [IQS7210A] = 0x4A,
+ [IQS7211A] = 0x59,
+ [IQS7211E] = 0x3E,
+ },
+ },
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "conversion period",
+ },
+ {
+ .name = "azoteq,thresh",
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x55,
+ [IQS7211A] = 0x67,
+ [IQS7211E] = 0x48,
+ },
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "threshold",
+ },
+ {
+ .name = "azoteq,contact-split",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x55,
+ [IQS7211A] = 0x67,
+ [IQS7211E] = 0x48,
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "contact split factor",
+ },
+ {
+ .name = "azoteq,trim-x",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x56,
+ [IQS7211E] = 0x49,
+ },
+ .reg_shift = 0,
+ .reg_width = 8,
+ .label = "horizontal trim width",
+ },
+ {
+ .name = "azoteq,trim-x",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7211A] = 0x68,
+ },
+ .label = "horizontal trim width",
+ },
+ {
+ .name = "azoteq,trim-y",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7210A] = 0x56,
+ [IQS7211E] = 0x49,
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .label = "vertical trim height",
+ },
+ {
+ .name = "azoteq,trim-y",
+ .reg_addr[IQS7211_REG_GRP_SYS] = {
+ [IQS7211A] = 0x69,
+ },
+ .label = "vertical trim height",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_key = IQS7211_REG_KEY_TAP,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x59,
+ [IQS7211A] = 0x81,
+ [IQS7211E] = 0x4C,
+ },
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-mid-ms",
+ .reg_key = IQS7211_REG_KEY_TAP,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7211E] = 0x4D,
+ },
+ .label = "repeated gesture time",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_key = IQS7211_REG_KEY_TAP,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x5A,
+ [IQS7211A] = 0x82,
+ [IQS7211E] = 0x4E,
+ },
+ .label = "gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x5A,
+ [IQS7211A] = 0x82,
+ [IQS7211E] = 0x4E,
+ },
+ .label = "gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-min-ms",
+ .reg_key = IQS7211_REG_KEY_HOLD,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x5B,
+ [IQS7211A] = 0x83,
+ [IQS7211E] = 0x4F,
+ },
+ .label = "minimum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x5C,
+ [IQS7211A] = 0x84,
+ [IQS7211E] = 0x50,
+ },
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-max-ms",
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x5C,
+ [IQS7211A] = 0x84,
+ [IQS7211E] = 0x50,
+ },
+ .label = "maximum gesture time",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x5D,
+ [IQS7211A] = 0x85,
+ [IQS7211E] = 0x51,
+ },
+ .label = "gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-dist",
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7210A] = 0x5E,
+ [IQS7211A] = 0x86,
+ [IQS7211E] = 0x52,
+ },
+ .label = "gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-dist-rep",
+ .reg_key = IQS7211_REG_KEY_AXIAL_X,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7211E] = 0x53,
+ },
+ .label = "repeated gesture distance",
+ },
+ {
+ .name = "azoteq,gesture-dist-rep",
+ .reg_key = IQS7211_REG_KEY_AXIAL_Y,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7211E] = 0x54,
+ },
+ .label = "repeated gesture distance",
+ },
+ {
+ .name = "azoteq,thresh",
+ .reg_key = IQS7211_REG_KEY_PALM,
+ .reg_addr[IQS7211_REG_GRP_TP] = {
+ [IQS7211E] = 0x55,
+ },
+ .reg_shift = 8,
+ .reg_width = 8,
+ .val_max = 42,
+ .label = "threshold",
+ },
+};
+
+static const u8 iqs7211_gesture_angle[] = {
+ 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C,
+ 0x0E, 0x0F, 0x10, 0x11,
+ 0x12, 0x14, 0x15, 0x16,
+ 0x17, 0x19, 0x1A, 0x1B,
+ 0x1C, 0x1E, 0x1F, 0x21,
+ 0x22, 0x23, 0x25, 0x26,
+ 0x28, 0x2A, 0x2B, 0x2D,
+ 0x2E, 0x30, 0x32, 0x34,
+ 0x36, 0x38, 0x3A, 0x3C,
+ 0x3E, 0x40, 0x42, 0x45,
+ 0x47, 0x4A, 0x4C, 0x4F,
+ 0x52, 0x55, 0x58, 0x5B,
+ 0x5F, 0x63, 0x66, 0x6B,
+ 0x6F, 0x73, 0x78, 0x7E,
+ 0x83, 0x89, 0x90, 0x97,
+ 0x9E, 0xA7, 0xB0, 0xBA,
+ 0xC5, 0xD1, 0xDF, 0xEF,
+};
+
+struct iqs7211_ver_info {
+ __le16 prod_num;
+ __le16 major;
+ __le16 minor;
+ __le32 patch;
+} __packed;
+
+struct iqs7211_touch_data {
+ __le16 abs_x;
+ __le16 abs_y;
+ __le16 pressure;
+ __le16 area;
+} __packed;
+
+struct iqs7211_tp_config {
+ u8 tp_settings;
+ u8 total_rx;
+ u8 total_tx;
+ u8 num_contacts;
+ __le16 max_x;
+ __le16 max_y;
+} __packed;
+
+struct iqs7211_private {
+ const struct iqs7211_dev_desc *dev_desc;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *irq_gpio;
+ struct i2c_client *client;
+ struct input_dev *tp_idev;
+ struct input_dev *kp_idev;
+ struct iqs7211_ver_info ver_info;
+ struct iqs7211_tp_config tp_config;
+ struct touchscreen_properties prop;
+ struct list_head reg_field_head;
+ enum iqs7211_comms_mode comms_init;
+ enum iqs7211_comms_mode comms_mode;
+ unsigned int num_contacts;
+ unsigned int kp_code[ARRAY_SIZE(iqs7211e_kp_events)];
+ u8 rx_tx_map[IQS7211_MAX_CTX + 1];
+ u8 cycle_alloc[2][33];
+ u8 exp_file[2];
+ u16 event_mask;
+ u16 ati_start;
+ u16 gesture_cache;
+};
+
+static int iqs7211_irq_poll(struct iqs7211_private *iqs7211, u64 timeout_us)
+{
+ int error, val;
+
+ error = readx_poll_timeout(gpiod_get_value_cansleep, iqs7211->irq_gpio,
+ val, val, IQS7211_COMMS_SLEEP_US, timeout_us);
+
+ return val < 0 ? val : error;
+}
+
+static int iqs7211_hard_reset(struct iqs7211_private *iqs7211)
+{
+ if (!iqs7211->reset_gpio)
+ return 0;
+
+ gpiod_set_value_cansleep(iqs7211->reset_gpio, 1);
+
+ /*
+ * The following delay ensures the shared RDY/MCLR pin is sampled in
+ * between periodic assertions by the device and assumes the default
+ * communication timeout has not been overwritten in OTP memory.
+ */
+ if (iqs7211->reset_gpio == iqs7211->irq_gpio)
+ msleep(IQS7211_RESET_TIMEOUT_MS);
+ else
+ usleep_range(1000, 1100);
+
+ gpiod_set_value_cansleep(iqs7211->reset_gpio, 0);
+ if (iqs7211->reset_gpio == iqs7211->irq_gpio)
+ iqs7211_irq_wait();
+
+ return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US);
+}
+
+static int iqs7211_force_comms(struct iqs7211_private *iqs7211)
+{
+ u8 msg_buf[] = { 0xFF, };
+ int ret;
+
+ switch (iqs7211->comms_mode) {
+ case IQS7211_COMMS_MODE_WAIT:
+ return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US);
+
+ case IQS7211_COMMS_MODE_FREE:
+ return 0;
+
+ case IQS7211_COMMS_MODE_FORCE:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * The device cannot communicate until it asserts its interrupt (RDY)
+ * pin. Attempts to do so while RDY is deasserted return an ACK; how-
+ * ever all write data is ignored, and all read data returns 0xEE.
+ *
+ * Unsolicited communication must be preceded by a special force com-
+ * munication command, after which the device eventually asserts its
+ * RDY pin and agrees to communicate.
+ *
+ * Regardless of whether communication is forced or the result of an
+ * interrupt, the device automatically deasserts its RDY pin once it
+ * detects an I2C stop condition, or a timeout expires.
+ */
+ ret = gpiod_get_value_cansleep(iqs7211->irq_gpio);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ return 0;
+
+ ret = i2c_master_send(iqs7211->client, msg_buf, sizeof(msg_buf));
+ if (ret < (int)sizeof(msg_buf)) {
+ if (ret >= 0)
+ ret = -EIO;
+
+ msleep(IQS7211_COMMS_RETRY_MS);
+ return ret;
+ }
+
+ iqs7211_irq_wait();
+
+ return iqs7211_irq_poll(iqs7211, IQS7211_COMMS_TIMEOUT_US);
+}
+
+static int iqs7211_read_burst(struct iqs7211_private *iqs7211,
+ u8 reg, void *val, u16 val_len)
+{
+ int ret, i;
+ struct i2c_client *client = iqs7211->client;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(reg),
+ .buf = ®,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = val_len,
+ .buf = (u8 *)val,
+ },
+ };
+
+ /*
+ * The following loop protects against an edge case in which the RDY
+ * pin is automatically deasserted just as the read is initiated. In
+ * that case, the read must be retried using forced communication.
+ */
+ for (i = 0; i < IQS7211_NUM_RETRIES; i++) {
+ ret = iqs7211_force_comms(iqs7211);
+ if (ret < 0)
+ continue;
+
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret < (int)ARRAY_SIZE(msg)) {
+ if (ret >= 0)
+ ret = -EIO;
+
+ msleep(IQS7211_COMMS_RETRY_MS);
+ continue;
+ }
+
+ if (get_unaligned_le16(msg[1].buf) == IQS7211_COMMS_ERROR) {
+ ret = -ENODATA;
+ continue;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ iqs7211_irq_wait();
+
+ if (ret < 0)
+ dev_err(&client->dev,
+ "Failed to read from address 0x%02X: %d\n", reg, ret);
+
+ return ret;
+}
+
+static int iqs7211_read_word(struct iqs7211_private *iqs7211, u8 reg, u16 *val)
+{
+ __le16 val_buf;
+ int error;
+
+ error = iqs7211_read_burst(iqs7211, reg, &val_buf, sizeof(val_buf));
+ if (error)
+ return error;
+
+ *val = le16_to_cpu(val_buf);
+
+ return 0;
+}
+
+static int iqs7211_write_burst(struct iqs7211_private *iqs7211,
+ u8 reg, const void *val, u16 val_len)
+{
+ int msg_len = sizeof(reg) + val_len;
+ int ret, i;
+ struct i2c_client *client = iqs7211->client;
+ u8 *msg_buf;
+
+ msg_buf = kzalloc(msg_len, GFP_KERNEL);
+ if (!msg_buf)
+ return -ENOMEM;
+
+ *msg_buf = reg;
+ memcpy(msg_buf + sizeof(reg), val, val_len);
+
+ /*
+ * The following loop protects against an edge case in which the RDY
+ * pin is automatically asserted just before the force communication
+ * command is sent.
+ *
+ * In that case, the subsequent I2C stop condition tricks the device
+ * into preemptively deasserting the RDY pin and the command must be
+ * sent again.
+ */
+ for (i = 0; i < IQS7211_NUM_RETRIES; i++) {
+ ret = iqs7211_force_comms(iqs7211);
+ if (ret < 0)
+ continue;
+
+ ret = i2c_master_send(client, msg_buf, msg_len);
+ if (ret < msg_len) {
+ if (ret >= 0)
+ ret = -EIO;
+
+ msleep(IQS7211_COMMS_RETRY_MS);
+ continue;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ kfree(msg_buf);
+
+ iqs7211_irq_wait();
+
+ if (ret < 0)
+ dev_err(&client->dev,
+ "Failed to write to address 0x%02X: %d\n", reg, ret);
+
+ return ret;
+}
+
+static int iqs7211_write_word(struct iqs7211_private *iqs7211, u8 reg, u16 val)
+{
+ __le16 val_buf = cpu_to_le16(val);
+
+ return iqs7211_write_burst(iqs7211, reg, &val_buf, sizeof(val_buf));
+}
+
+static int iqs7211_start_comms(struct iqs7211_private *iqs7211)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct i2c_client *client = iqs7211->client;
+ bool forced_comms;
+ unsigned int val;
+ u16 comms_setup;
+ int error;
+
+ /*
+ * Until forced communication can be enabled, the host must wait for a
+ * communication window each time it intends to elicit a response from
+ * the device.
+ *
+ * Forced communication is not necessary, however, if the host adapter
+ * can support clock stretching. In that case, the device freely clock
+ * stretches until all pending conversions are complete.
+ */
+ forced_comms = device_property_present(&client->dev,
+ "azoteq,forced-comms");
+
+ error = device_property_read_u32(&client->dev,
+ "azoteq,forced-comms-default", &val);
+ if (error == -EINVAL) {
+ iqs7211->comms_init = IQS7211_COMMS_MODE_WAIT;
+ } else if (error) {
+ dev_err(&client->dev,
+ "Failed to read default communication mode: %d\n",
+ error);
+ return error;
+ } else if (val) {
+ iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_FORCE
+ : IQS7211_COMMS_MODE_WAIT;
+ } else {
+ iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_WAIT
+ : IQS7211_COMMS_MODE_FREE;
+ }
+
+ iqs7211->comms_mode = iqs7211->comms_init;
+
+ error = iqs7211_hard_reset(iqs7211);
+ if (error) {
+ dev_err(&client->dev, "Failed to reset device: %d\n", error);
+ return error;
+ }
+
+ error = iqs7211_read_burst(iqs7211, IQS7211_PROD_NUM,
+ &iqs7211->ver_info,
+ sizeof(iqs7211->ver_info));
+ if (error)
+ return error;
+
+ if (le16_to_cpu(iqs7211->ver_info.prod_num) != dev_desc->prod_num) {
+ dev_err(&client->dev, "Invalid product number: %u\n",
+ le16_to_cpu(iqs7211->ver_info.prod_num));
+ return -EINVAL;
+ }
+
+ error = iqs7211_read_word(iqs7211, dev_desc->sys_ctrl + 1,
+ &comms_setup);
+ if (error)
+ return error;
+
+ if (forced_comms)
+ comms_setup |= dev_desc->comms_req;
+ else
+ comms_setup &= ~dev_desc->comms_req;
+
+ error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
+ comms_setup | dev_desc->comms_end);
+ if (error)
+ return error;
+
+ if (forced_comms)
+ iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE;
+ else
+ iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE;
+
+ error = iqs7211_read_burst(iqs7211, dev_desc->exp_file,
+ iqs7211->exp_file,
+ sizeof(iqs7211->exp_file));
+ if (error)
+ return error;
+
+ error = iqs7211_read_burst(iqs7211, dev_desc->tp_config,
+ &iqs7211->tp_config,
+ sizeof(iqs7211->tp_config));
+ if (error)
+ return error;
+
+ error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
+ comms_setup);
+ if (error)
+ return error;
+
+ iqs7211->event_mask = comms_setup & ~IQS7211_EVENT_MASK_ALL;
+ iqs7211->event_mask |= (IQS7211_EVENT_MASK_ATI | IQS7211_EVENT_MODE);
+
+ return 0;
+}
+
+static int iqs7211_init_device(struct iqs7211_private *iqs7211)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct iqs7211_reg_field_desc *reg_field;
+ __le16 sys_ctrl[] = {
+ cpu_to_le16(dev_desc->ack_reset),
+ cpu_to_le16(iqs7211->event_mask),
+ };
+ int error, i;
+
+ /*
+ * Acknowledge reset before writing any registers in case the device
+ * suffers a spurious reset during initialization. The communication
+ * mode is configured at this time as well.
+ */
+ error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
+ sizeof(sys_ctrl));
+ if (error)
+ return error;
+
+ if (iqs7211->event_mask & dev_desc->comms_req)
+ iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE;
+ else
+ iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE;
+
+ /*
+ * Take advantage of the stop-bit disable function, if available, to
+ * save the trouble of having to reopen a communication window after
+ * each read or write.
+ */
+ error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
+ iqs7211->event_mask | dev_desc->comms_end);
+ if (error)
+ return error;
+
+ list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) {
+ u16 new_val = reg_field->val;
+
+ if (reg_field->mask < U16_MAX) {
+ u16 old_val;
+
+ error = iqs7211_read_word(iqs7211, reg_field->addr,
+ &old_val);
+ if (error)
+ return error;
+
+ new_val = old_val & ~reg_field->mask;
+ new_val |= reg_field->val;
+
+ if (new_val == old_val)
+ continue;
+ }
+
+ error = iqs7211_write_word(iqs7211, reg_field->addr, new_val);
+ if (error)
+ return error;
+ }
+
+ error = iqs7211_write_burst(iqs7211, dev_desc->tp_config,
+ &iqs7211->tp_config,
+ sizeof(iqs7211->tp_config));
+ if (error)
+ return error;
+
+ if (**iqs7211->cycle_alloc) {
+ error = iqs7211_write_burst(iqs7211, dev_desc->rx_tx_map,
+ &iqs7211->rx_tx_map,
+ dev_desc->num_ctx);
+ if (error)
+ return error;
+
+ for (i = 0; i < sizeof(dev_desc->cycle_limit); i++) {
+ error = iqs7211_write_burst(iqs7211,
+ dev_desc->cycle_alloc[i],
+ iqs7211->cycle_alloc[i],
+ dev_desc->cycle_limit[i] * 3);
+ if (error)
+ return error;
+ }
+ }
+
+ *sys_ctrl = cpu_to_le16(iqs7211->ati_start);
+
+ return iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
+ sizeof(sys_ctrl));
+}
+
+static int iqs7211_add_field(struct iqs7211_private *iqs7211,
+ struct iqs7211_reg_field_desc new_field)
+{
+ struct i2c_client *client = iqs7211->client;
+ struct iqs7211_reg_field_desc *reg_field;
+
+ if (!new_field.addr)
+ return 0;
+
+ list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) {
+ if (reg_field->addr != new_field.addr)
+ continue;
+
+ reg_field->mask |= new_field.mask;
+ reg_field->val |= new_field.val;
+ return 0;
+ }
+
+ reg_field = devm_kzalloc(&client->dev, sizeof(*reg_field), GFP_KERNEL);
+ if (!reg_field)
+ return -ENOMEM;
+
+ reg_field->addr = new_field.addr;
+ reg_field->mask = new_field.mask;
+ reg_field->val = new_field.val;
+
+ list_add(®_field->list, &iqs7211->reg_field_head);
+
+ return 0;
+}
+
+static int iqs7211_parse_props(struct iqs7211_private *iqs7211,
+ struct fwnode_handle *reg_grp_node,
+ enum iqs7211_reg_grp_id reg_grp,
+ enum iqs7211_reg_key_id reg_key)
+{
+ struct i2c_client *client = iqs7211->client;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(iqs7211_props); i++) {
+ const char *name = iqs7211_props[i].name;
+ u8 reg_addr = iqs7211_props[i].reg_addr[reg_grp]
+ [iqs7211->dev_desc -
+ iqs7211_devs];
+ int reg_shift = iqs7211_props[i].reg_shift;
+ int reg_width = iqs7211_props[i].reg_width ? : 16;
+ int val_pitch = iqs7211_props[i].val_pitch ? : 1;
+ int val_min = iqs7211_props[i].val_min;
+ int val_max = iqs7211_props[i].val_max;
+ const char *label = iqs7211_props[i].label ? : name;
+ struct iqs7211_reg_field_desc reg_field;
+ unsigned int val;
+ int error;
+
+ if (iqs7211_props[i].reg_key != reg_key)
+ continue;
+
+ if (!reg_addr)
+ continue;
+
+ error = fwnode_property_read_u32(reg_grp_node, name, &val);
+ if (error == -EINVAL) {
+ continue;
+ } else if (error) {
+ dev_err(&client->dev, "Failed to read %s %s: %d\n",
+ fwnode_get_name(reg_grp_node), label, error);
+ return error;
+ }
+
+ if (!val_max)
+ val_max = GENMASK(reg_width - 1, 0) * val_pitch;
+
+ if (val < val_min || val > val_max) {
+ dev_err(&client->dev, "Invalid %s: %u\n", label, val);
+ return -EINVAL;
+ }
+
+ reg_field.addr = reg_addr;
+ reg_field.mask = GENMASK(reg_shift + reg_width - 1, reg_shift);
+ reg_field.val = val / val_pitch << reg_shift;
+
+ error = iqs7211_add_field(iqs7211, reg_field);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int iqs7211_parse_event(struct iqs7211_private *iqs7211,
+ struct fwnode_handle *event_node,
+ enum iqs7211_reg_grp_id reg_grp,
+ enum iqs7211_reg_key_id reg_key,
+ unsigned int *event_code)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct i2c_client *client = iqs7211->client;
+ struct iqs7211_reg_field_desc reg_field;
+ unsigned int val;
+ int error;
+
+ error = iqs7211_parse_props(iqs7211, event_node, reg_grp, reg_key);
+ if (error)
+ return error;
+
+ if (reg_key == IQS7211_REG_KEY_AXIAL_X ||
+ reg_key == IQS7211_REG_KEY_AXIAL_Y) {
+ error = fwnode_property_read_u32(event_node,
+ "azoteq,gesture-angle", &val);
+ if (!error) {
+ if (val >= ARRAY_SIZE(iqs7211_gesture_angle)) {
+ dev_err(&client->dev,
+ "Invalid %s gesture angle: %u\n",
+ fwnode_get_name(event_node), val);
+ return -EINVAL;
+ }
+
+ reg_field.addr = dev_desc->gesture_angle;
+ reg_field.mask = U8_MAX;
+ reg_field.val = iqs7211_gesture_angle[val];
+
+ error = iqs7211_add_field(iqs7211, reg_field);
+ if (error)
+ return error;
+ } else if (error != -EINVAL) {
+ dev_err(&client->dev,
+ "Failed to read %s gesture angle: %d\n",
+ fwnode_get_name(event_node), error);
+ return error;
+ }
+ }
+
+ error = fwnode_property_read_u32(event_node, "linux,code", event_code);
+ if (error == -EINVAL)
+ error = 0;
+ else if (error)
+ dev_err(&client->dev, "Failed to read %s code: %d\n",
+ fwnode_get_name(event_node), error);
+
+ return error;
+}
+
+static int iqs7211_parse_cycles(struct iqs7211_private *iqs7211,
+ struct fwnode_handle *tp_node)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct i2c_client *client = iqs7211->client;
+ int num_cycles = dev_desc->cycle_limit[0] + dev_desc->cycle_limit[1];
+ int error, count, i, j, k, cycle_start;
+ unsigned int cycle_alloc[IQS7211_MAX_CYCLES][2];
+ u8 total_rx = iqs7211->tp_config.total_rx;
+ u8 total_tx = iqs7211->tp_config.total_tx;
+
+ for (i = 0; i < IQS7211_MAX_CYCLES * 2; i++)
+ *(cycle_alloc[0] + i) = U8_MAX;
+
+ count = fwnode_property_count_u32(tp_node, "azoteq,channel-select");
+ if (count == -EINVAL) {
+ /*
+ * Assign each sensing cycle's slots (0 and 1) to a channel,
+ * defined as the intersection between two CRx and CTx pins.
+ * A channel assignment of 255 means the slot is unused.
+ */
+ for (i = 0, cycle_start = 0; i < total_tx; i++) {
+ int cycle_stop = 0;
+
+ for (j = 0; j < total_rx; j++) {
+ /*
+ * Channels formed by CRx0-3 and CRx4-7 are
+ * bound to slots 0 and 1, respectively.
+ */
+ int slot = iqs7211->rx_tx_map[j] < 4 ? 0 : 1;
+ int chan = i * total_rx + j;
+
+ for (k = cycle_start; k < num_cycles; k++) {
+ if (cycle_alloc[k][slot] < U8_MAX)
+ continue;
+
+ cycle_alloc[k][slot] = chan;
+ break;
+ }
+
+ if (k < num_cycles) {
+ cycle_stop = max(k, cycle_stop);
+ continue;
+ }
+
+ dev_err(&client->dev,
+ "Insufficient number of cycles\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Sensing cycles cannot straddle more than one CTx
+ * pin. As such, the next row's starting cycle must
+ * be greater than the previous row's highest cycle.
+ */
+ cycle_start = cycle_stop + 1;
+ }
+ } else if (count < 0) {
+ dev_err(&client->dev, "Failed to count channels: %d\n", count);
+ return count;
+ } else if (count > num_cycles * 2) {
+ dev_err(&client->dev, "Insufficient number of cycles\n");
+ return -EINVAL;
+ } else if (count > 0) {
+ error = fwnode_property_read_u32_array(tp_node,
+ "azoteq,channel-select",
+ cycle_alloc[0], count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read channels: %d\n",
+ error);
+ return error;
+ }
+
+ for (i = 0; i < count; i++) {
+ int chan = *(cycle_alloc[0] + i);
+
+ if (chan == U8_MAX)
+ continue;
+
+ if (chan >= total_rx * total_tx) {
+ dev_err(&client->dev, "Invalid channel: %d\n",
+ chan);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < count; j++) {
+ if (j == i || *(cycle_alloc[0] + j) != chan)
+ continue;
+
+ dev_err(&client->dev, "Duplicate channel: %d\n",
+ chan);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /*
+ * Once the raw channel assignments have been derived, they must be
+ * packed according to the device's register map.
+ */
+ for (i = 0, cycle_start = 0; i < sizeof(dev_desc->cycle_limit); i++) {
+ int offs = 0;
+
+ for (j = cycle_start;
+ j < cycle_start + dev_desc->cycle_limit[i]; j++) {
+ iqs7211->cycle_alloc[i][offs++] = 0x05;
+ iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][0];
+ iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][1];
+ }
+
+ cycle_start += dev_desc->cycle_limit[i];
+ }
+
+ return 0;
+}
+
+static int iqs7211_parse_tp(struct iqs7211_private *iqs7211,
+ struct fwnode_handle *tp_node)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct i2c_client *client = iqs7211->client;
+ unsigned int pins[IQS7211_MAX_CTX];
+ int error, count, i, j;
+
+ count = fwnode_property_count_u32(tp_node, "azoteq,rx-enable");
+ if (count == -EINVAL) {
+ return 0;
+ } else if (count < 0) {
+ dev_err(&client->dev, "Failed to count CRx pins: %d\n", count);
+ return count;
+ } else if (count > IQS7211_NUM_CRX) {
+ dev_err(&client->dev, "Invalid number of CRx pins\n");
+ return -EINVAL;
+ }
+
+ error = fwnode_property_read_u32_array(tp_node, "azoteq,rx-enable",
+ pins, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read CRx pins: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (pins[i] >= IQS7211_NUM_CRX) {
+ dev_err(&client->dev, "Invalid CRx pin: %u\n", pins[i]);
+ return -EINVAL;
+ }
+
+ iqs7211->rx_tx_map[i] = pins[i];
+ }
+
+ iqs7211->tp_config.total_rx = count;
+
+ count = fwnode_property_count_u32(tp_node, "azoteq,tx-enable");
+ if (count < 0) {
+ dev_err(&client->dev, "Failed to count CTx pins: %d\n", count);
+ return count;
+ } else if (count > dev_desc->num_ctx) {
+ dev_err(&client->dev, "Invalid number of CTx pins\n");
+ return -EINVAL;
+ }
+
+ error = fwnode_property_read_u32_array(tp_node, "azoteq,tx-enable",
+ pins, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read CTx pins: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (pins[i] >= dev_desc->num_ctx) {
+ dev_err(&client->dev, "Invalid CTx pin: %u\n", pins[i]);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < iqs7211->tp_config.total_rx; j++) {
+ if (iqs7211->rx_tx_map[j] != pins[i])
+ continue;
+
+ dev_err(&client->dev, "Conflicting CTx pin: %u\n",
+ pins[i]);
+ return -EINVAL;
+ }
+
+ iqs7211->rx_tx_map[iqs7211->tp_config.total_rx + i] = pins[i];
+ }
+
+ iqs7211->tp_config.total_tx = count;
+
+ return iqs7211_parse_cycles(iqs7211, tp_node);
+}
+
+static int iqs7211_parse_alp(struct iqs7211_private *iqs7211,
+ struct fwnode_handle *alp_node)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct i2c_client *client = iqs7211->client;
+ struct iqs7211_reg_field_desc reg_field;
+ int error, count, i;
+
+ count = fwnode_property_count_u32(alp_node, "azoteq,rx-enable");
+ if (count < 0 && count != -EINVAL) {
+ dev_err(&client->dev, "Failed to count CRx pins: %d\n", count);
+ return count;
+ } else if (count > IQS7211_NUM_CRX) {
+ dev_err(&client->dev, "Invalid number of CRx pins\n");
+ return -EINVAL;
+ } else if (count >= 0) {
+ unsigned int pins[IQS7211_NUM_CRX];
+
+ error = fwnode_property_read_u32_array(alp_node,
+ "azoteq,rx-enable",
+ pins, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read CRx pins: %d\n",
+ error);
+ return error;
+ }
+
+ reg_field.addr = dev_desc->alp_config;
+ reg_field.mask = GENMASK(IQS7211_NUM_CRX - 1, 0);
+ reg_field.val = 0;
+
+ for (i = 0; i < count; i++) {
+ if (pins[i] < dev_desc->min_crx_alp ||
+ pins[i] >= IQS7211_NUM_CRX) {
+ dev_err(&client->dev, "Invalid CRx pin: %u\n",
+ pins[i]);
+ return -EINVAL;
+ }
+
+ reg_field.val |= BIT(pins[i]);
+ }
+
+ error = iqs7211_add_field(iqs7211, reg_field);
+ if (error)
+ return error;
+ }
+
+ count = fwnode_property_count_u32(alp_node, "azoteq,tx-enable");
+ if (count < 0 && count != -EINVAL) {
+ dev_err(&client->dev, "Failed to count CTx pins: %d\n", count);
+ return count;
+ } else if (count > dev_desc->num_ctx) {
+ dev_err(&client->dev, "Invalid number of CTx pins\n");
+ return -EINVAL;
+ } else if (count >= 0) {
+ unsigned int pins[IQS7211_MAX_CTX];
+
+ error = fwnode_property_read_u32_array(alp_node,
+ "azoteq,tx-enable",
+ pins, count);
+ if (error) {
+ dev_err(&client->dev, "Failed to read CTx pins: %d\n",
+ error);
+ return error;
+ }
+
+ reg_field.addr = dev_desc->alp_config + 1;
+ reg_field.mask = GENMASK(dev_desc->num_ctx - 1, 0);
+ reg_field.val = 0;
+
+ for (i = 0; i < count; i++) {
+ if (pins[i] >= dev_desc->num_ctx) {
+ dev_err(&client->dev, "Invalid CTx pin: %u\n",
+ pins[i]);
+ return -EINVAL;
+ }
+
+ reg_field.val |= BIT(pins[i]);
+ }
+
+ error = iqs7211_add_field(iqs7211, reg_field);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int (*iqs7211_parse_extra[IQS7211_NUM_REG_GRPS])
+ (struct iqs7211_private *iqs7211,
+ struct fwnode_handle *reg_grp_node) = {
+ [IQS7211_REG_GRP_TP] = iqs7211_parse_tp,
+ [IQS7211_REG_GRP_ALP] = iqs7211_parse_alp,
+};
+
+static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211,
+ struct fwnode_handle *reg_grp_node,
+ enum iqs7211_reg_grp_id reg_grp)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct iqs7211_reg_field_desc reg_field;
+ int error, i;
+
+ error = iqs7211_parse_props(iqs7211, reg_grp_node, reg_grp,
+ IQS7211_REG_KEY_NONE);
+ if (error)
+ return error;
+
+ if (iqs7211_parse_extra[reg_grp]) {
+ error = iqs7211_parse_extra[reg_grp](iqs7211, reg_grp_node);
+ if (error)
+ return error;
+ }
+
+ iqs7211->ati_start |= dev_desc->ati_start[reg_grp];
+
+ reg_field.addr = dev_desc->kp_enable[reg_grp];
+ reg_field.mask = 0;
+ reg_field.val = 0;
+
+ for (i = 0; i < dev_desc->num_kp_events; i++) {
+ const char *event_name = dev_desc->kp_events[i].name;
+ struct fwnode_handle *event_node;
+
+ if (dev_desc->kp_events[i].reg_grp != reg_grp)
+ continue;
+
+ reg_field.mask |= dev_desc->kp_events[i].enable;
+
+ if (event_name)
+ event_node = fwnode_get_named_child_node(reg_grp_node,
+ event_name);
+ else
+ event_node = fwnode_handle_get(reg_grp_node);
+
+ if (!event_node)
+ continue;
+
+ error = iqs7211_parse_event(iqs7211, event_node,
+ dev_desc->kp_events[i].reg_grp,
+ dev_desc->kp_events[i].reg_key,
+ &iqs7211->kp_code[i]);
+ fwnode_handle_put(event_node);
+ if (error)
+ return error;
+
+ reg_field.val |= dev_desc->kp_events[i].enable;
+
+ iqs7211->event_mask |= iqs7211_reg_grp_masks[reg_grp];
+ }
+
+ return iqs7211_add_field(iqs7211, reg_field);
+}
+
+static int iqs7211_register_kp(struct iqs7211_private *iqs7211)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct input_dev *kp_idev = iqs7211->kp_idev;
+ struct i2c_client *client = iqs7211->client;
+ int error, i;
+
+ for (i = 0; i < dev_desc->num_kp_events; i++)
+ if (iqs7211->kp_code[i])
+ break;
+
+ if (i == dev_desc->num_kp_events)
+ return 0;
+
+ kp_idev = devm_input_allocate_device(&client->dev);
+ if (!kp_idev)
+ return -ENOMEM;
+
+ iqs7211->kp_idev = kp_idev;
+
+ kp_idev->name = dev_desc->kp_name;
+ kp_idev->id.bustype = BUS_I2C;
+
+ for (i = 0; i < dev_desc->num_kp_events; i++)
+ if (iqs7211->kp_code[i])
+ input_set_capability(iqs7211->kp_idev, EV_KEY,
+ iqs7211->kp_code[i]);
+
+ error = input_register_device(kp_idev);
+ if (error)
+ dev_err(&client->dev, "Failed to register %s: %d\n",
+ kp_idev->name, error);
+
+ return error;
+}
+
+static int iqs7211_register_tp(struct iqs7211_private *iqs7211)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct touchscreen_properties *prop = &iqs7211->prop;
+ struct input_dev *tp_idev = iqs7211->tp_idev;
+ struct i2c_client *client = iqs7211->client;
+ int error;
+
+ error = device_property_read_u32(&client->dev, "azoteq,num-contacts",
+ &iqs7211->num_contacts);
+ if (error == -EINVAL) {
+ return 0;
+ } else if (error) {
+ dev_err(&client->dev, "Failed to read number of contacts: %d\n",
+ error);
+ return error;
+ } else if (iqs7211->num_contacts > IQS7211_MAX_CONTACTS) {
+ dev_err(&client->dev, "Invalid number of contacts: %u\n",
+ iqs7211->num_contacts);
+ return -EINVAL;
+ }
+
+ iqs7211->tp_config.num_contacts = iqs7211->num_contacts ? : 1;
+
+ if (!iqs7211->num_contacts)
+ return 0;
+
+ iqs7211->event_mask |= IQS7211_EVENT_MASK_MOVE;
+
+ tp_idev = devm_input_allocate_device(&client->dev);
+ if (!tp_idev)
+ return -ENOMEM;
+
+ iqs7211->tp_idev = tp_idev;
+
+ tp_idev->name = dev_desc->tp_name;
+ tp_idev->id.bustype = BUS_I2C;
+
+ input_set_abs_params(tp_idev, ABS_MT_POSITION_X,
+ 0, le16_to_cpu(iqs7211->tp_config.max_x), 0, 0);
+
+ input_set_abs_params(tp_idev, ABS_MT_POSITION_Y,
+ 0, le16_to_cpu(iqs7211->tp_config.max_y), 0, 0);
+
+ input_set_abs_params(tp_idev, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
+
+ touchscreen_parse_properties(tp_idev, true, prop);
+
+ /*
+ * The device reserves 0xFFFF for coordinates that correspond to slots
+ * which are not in a state of touch.
+ */
+ if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
+ dev_err(&client->dev, "Invalid trackpad size: %u*%u\n",
+ prop->max_x, prop->max_y);
+ return -EINVAL;
+ }
+
+ iqs7211->tp_config.max_x = cpu_to_le16(prop->max_x);
+ iqs7211->tp_config.max_y = cpu_to_le16(prop->max_y);
+
+ error = input_mt_init_slots(tp_idev, iqs7211->num_contacts,
+ INPUT_MT_DIRECT);
+ if (error) {
+ dev_err(&client->dev, "Failed to initialize slots: %d\n",
+ error);
+ return error;
+ }
+
+ error = input_register_device(tp_idev);
+ if (error)
+ dev_err(&client->dev, "Failed to register %s: %d\n",
+ tp_idev->name, error);
+
+ return error;
+}
+
+static int iqs7211_report(struct iqs7211_private *iqs7211)
+{
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ struct i2c_client *client = iqs7211->client;
+ struct iqs7211_touch_data *touch_data;
+ u16 info_flags, charge_mode, gesture_flags;
+ __le16 status[12];
+ int error, i;
+
+ error = iqs7211_read_burst(iqs7211, dev_desc->sys_stat, status,
+ dev_desc->contact_offs * sizeof(__le16) +
+ iqs7211->num_contacts * sizeof(*touch_data));
+ if (error)
+ return error;
+
+ info_flags = le16_to_cpu(status[dev_desc->info_offs]);
+
+ if (info_flags & dev_desc->show_reset) {
+ dev_err(&client->dev, "Unexpected device reset\n");
+
+ /*
+ * The device may or may not expect forced communication after
+ * it exits hardware reset, so the corresponding state machine
+ * must be reset as well.
+ */
+ iqs7211->comms_mode = iqs7211->comms_init;
+
+ return iqs7211_init_device(iqs7211);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dev_desc->ati_error); i++) {
+ if (!(info_flags & dev_desc->ati_error[i]))
+ continue;
+
+ dev_err(&client->dev, "Unexpected %s ATI error\n",
+ iqs7211_reg_grp_names[i]);
+ return 0;
+ }
+
+ for (i = 0; i < iqs7211->num_contacts; i++) {
+ u16 pressure;
+
+ touch_data = (struct iqs7211_touch_data *)
+ &status[dev_desc->contact_offs] + i;
+ pressure = le16_to_cpu(touch_data->pressure);
+
+ input_mt_slot(iqs7211->tp_idev, i);
+ if (input_mt_report_slot_state(iqs7211->tp_idev, MT_TOOL_FINGER,
+ pressure != 0)) {
+ touchscreen_report_pos(iqs7211->tp_idev, &iqs7211->prop,
+ le16_to_cpu(touch_data->abs_x),
+ le16_to_cpu(touch_data->abs_y),
+ true);
+ input_report_abs(iqs7211->tp_idev, ABS_MT_PRESSURE,
+ pressure);
+ }
+ }
+
+ if (iqs7211->num_contacts) {
+ input_mt_sync_frame(iqs7211->tp_idev);
+ input_sync(iqs7211->tp_idev);
+ }
+
+ if (!iqs7211->kp_idev)
+ return 0;
+
+ charge_mode = info_flags & GENMASK(dev_desc->charge_shift + 2,
+ dev_desc->charge_shift);
+ charge_mode >>= dev_desc->charge_shift;
+
+ /*
+ * A charging mode higher than 2 (idle mode) indicates the device last
+ * operated in low-power mode and intends to express an ALP event.
+ */
+ if (info_flags & dev_desc->kp_events->mask && charge_mode > 2) {
+ input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 1);
+ input_sync(iqs7211->kp_idev);
+
+ input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 0);
+ }
+
+ for (i = 0; i < dev_desc->num_kp_events; i++) {
+ if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_BTN)
+ continue;
+
+ input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i],
+ info_flags & dev_desc->kp_events[i].mask);
+ }
+
+ gesture_flags = le16_to_cpu(status[dev_desc->gesture_offs]);
+
+ for (i = 0; i < dev_desc->num_kp_events; i++) {
+ enum iqs7211_reg_key_id reg_key = dev_desc->kp_events[i].reg_key;
+ u16 mask = dev_desc->kp_events[i].mask;
+
+ if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_TP)
+ continue;
+
+ if ((gesture_flags ^ iqs7211->gesture_cache) & mask)
+ input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i],
+ gesture_flags & mask);
+
+ iqs7211->gesture_cache &= ~mask;
+
+ /*
+ * Hold and palm gestures persist while the contact remains in
+ * place; all others are momentary and hence are followed by a
+ * complementary release event.
+ */
+ if (reg_key == IQS7211_REG_KEY_HOLD ||
+ reg_key == IQS7211_REG_KEY_PALM) {
+ iqs7211->gesture_cache |= gesture_flags & mask;
+ gesture_flags &= ~mask;
+ }
+ }
+
+ if (gesture_flags) {
+ input_sync(iqs7211->kp_idev);
+
+ for (i = 0; i < dev_desc->num_kp_events; i++)
+ if (dev_desc->kp_events[i].reg_grp == IQS7211_REG_GRP_TP &&
+ gesture_flags & dev_desc->kp_events[i].mask)
+ input_report_key(iqs7211->kp_idev,
+ iqs7211->kp_code[i], 0);
+ }
+
+ input_sync(iqs7211->kp_idev);
+
+ return 0;
+}
+
+static irqreturn_t iqs7211_irq(int irq, void *context)
+{
+ struct iqs7211_private *iqs7211 = context;
+
+ return iqs7211_report(iqs7211) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static int iqs7211_suspend(struct device *dev)
+{
+ struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ int error;
+
+ if (!dev_desc->suspend || device_may_wakeup(dev))
+ return 0;
+
+ /*
+ * I2C communication prompts the device to assert its RDY pin if it is
+ * not already asserted. As such, the interrupt must be disabled so as
+ * to prevent reentrant interrupts.
+ */
+ disable_irq(gpiod_to_irq(iqs7211->irq_gpio));
+
+ error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl,
+ dev_desc->suspend);
+
+ enable_irq(gpiod_to_irq(iqs7211->irq_gpio));
+
+ return error;
+}
+
+static int iqs7211_resume(struct device *dev)
+{
+ struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
+ const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
+ __le16 sys_ctrl[] = {
+ 0,
+ cpu_to_le16(iqs7211->event_mask),
+ };
+ int error;
+
+ if (!dev_desc->suspend || device_may_wakeup(dev))
+ return 0;
+
+ disable_irq(gpiod_to_irq(iqs7211->irq_gpio));
+
+ /*
+ * Forced communication, if in use, must be explicitly enabled as part
+ * of the wake-up command.
+ */
+ error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
+ sizeof(sys_ctrl));
+
+ enable_irq(gpiod_to_irq(iqs7211->irq_gpio));
+
+ return error;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(iqs7211_pm, iqs7211_suspend, iqs7211_resume);
+
+static ssize_t fw_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n",
+ le16_to_cpu(iqs7211->ver_info.prod_num),
+ le32_to_cpu(iqs7211->ver_info.patch),
+ le16_to_cpu(iqs7211->ver_info.major),
+ le16_to_cpu(iqs7211->ver_info.minor),
+ iqs7211->exp_file[1], iqs7211->exp_file[0]);
+}
+
+static DEVICE_ATTR_RO(fw_info);
+
+static struct attribute *iqs7211_attrs[] = {
+ &dev_attr_fw_info.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(iqs7211);
+
+static const struct of_device_id iqs7211_of_match[] = {
+ {
+ .compatible = "azoteq,iqs7210a",
+ .data = (void *)IQS7210A,
+ },
+ {
+ .compatible = "azoteq,iqs7211a",
+ .data = (void *)IQS7211A,
+ },
+ {
+ .compatible = "azoteq,iqs7211e",
+ .data = (void *)IQS7211E,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, iqs7211_of_match);
+
+static const struct i2c_device_id iqs7211_id[] = {
+ { "iqs7210a", IQS7210A },
+ { "iqs7211a", IQS7211A },
+ { "iqs7211e", IQS7211E },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, iqs7211_id);
+
+static int iqs7211_probe(struct i2c_client *client)
+{
+ struct iqs7211_private *iqs7211;
+ enum iqs7211_reg_grp_id reg_grp;
+ enum iqs7211_dev_id dev_id;
+ unsigned long irq_flags;
+ bool shared_irq;
+ int error, irq;
+
+ iqs7211 = devm_kzalloc(&client->dev, sizeof(*iqs7211), GFP_KERNEL);
+ if (!iqs7211)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, iqs7211);
+ iqs7211->client = client;
+
+ INIT_LIST_HEAD(&iqs7211->reg_field_head);
+
+ if (client->dev.of_node)
+ dev_id = (enum iqs7211_dev_id)of_device_get_match_data(&client->dev);
+ else
+ dev_id = i2c_match_id(iqs7211_id, client)->driver_data;
+
+ shared_irq = iqs7211_devs[dev_id].num_ctx == IQS7211_MAX_CTX;
+ iqs7211->dev_desc = &iqs7211_devs[dev_id];
+
+ /*
+ * The RDY pin behaves as an interrupt, but must also be polled ahead
+ * of unsolicited I2C communication. As such, it is first opened as a
+ * GPIO and then passed to gpiod_to_irq() to register the interrupt.
+ *
+ * If an extra CTx pin is present, the RDY and MCLR pins are combined
+ * into a single bidirectional pin. In that case, the platform's GPIO
+ * must be configured as an open-drain output.
+ */
+ iqs7211->irq_gpio = devm_gpiod_get(&client->dev, "irq",
+ shared_irq ? GPIOD_OUT_LOW
+ : GPIOD_IN);
+ if (IS_ERR(iqs7211->irq_gpio)) {
+ error = PTR_ERR(iqs7211->irq_gpio);
+ dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n",
+ error);
+ return error;
+ }
+
+ if (shared_irq) {
+ iqs7211->reset_gpio = iqs7211->irq_gpio;
+ } else {
+ iqs7211->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(iqs7211->reset_gpio)) {
+ error = PTR_ERR(iqs7211->reset_gpio);
+ dev_err(&client->dev,
+ "Failed to request reset GPIO: %d\n", error);
+ return error;
+ }
+ }
+
+ error = iqs7211_start_comms(iqs7211);
+ if (error)
+ return error;
+
+ for (reg_grp = 0; reg_grp < IQS7211_NUM_REG_GRPS; reg_grp++) {
+ const char *reg_grp_name = iqs7211_reg_grp_names[reg_grp];
+ struct fwnode_handle *reg_grp_node;
+
+ if (reg_grp_name)
+ reg_grp_node = device_get_named_child_node(&client->dev,
+ reg_grp_name);
+ else
+ reg_grp_node = fwnode_handle_get(dev_fwnode(&client->dev));
+
+ if (!reg_grp_node)
+ continue;
+
+ error = iqs7211_parse_reg_grp(iqs7211, reg_grp_node, reg_grp);
+ fwnode_handle_put(reg_grp_node);
+ if (error)
+ return error;
+ }
+
+ error = iqs7211_register_kp(iqs7211);
+ if (error)
+ return error;
+
+ error = iqs7211_register_tp(iqs7211);
+ if (error)
+ return error;
+
+ error = iqs7211_init_device(iqs7211);
+ if (error)
+ return error;
+
+ irq = gpiod_to_irq(iqs7211->irq_gpio);
+ if (irq < 0)
+ return irq;
+
+ irq_flags = gpiod_is_active_low(iqs7211->irq_gpio) ? IRQF_TRIGGER_LOW
+ : IRQF_TRIGGER_HIGH;
+ irq_flags |= IRQF_ONESHOT;
+
+ error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7211_irq,
+ irq_flags, client->name, iqs7211);
+ if (error)
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+
+ return error;
+}
+
+static struct i2c_driver iqs7211_i2c_driver = {
+ .probe = iqs7211_probe,
+ .id_table = iqs7211_id,
+ .driver = {
+ .name = "iqs7211",
+ .of_match_table = iqs7211_of_match,
+ .dev_groups = iqs7211_groups,
+ .pm = pm_sleep_ptr(&iqs7211_pm),
+ },
+};
+module_i2c_driver(iqs7211_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* Re: [PATCH] selftests/input: Introduce basic tests for evdev ioctls
From: Peter Hutterer @ 2023-05-30 5:56 UTC (permalink / raw)
To: eballetbo, linux-kselftest, linux-kernel
Cc: Shuah Khan, Dmitry Torokhov, Javier Martinez Canillas,
Dana Elfassy, linux-input, phuttere, Benjamin Tissoires
In-Reply-To: <20230526082205.6297-1-eballetbo@kernel.org>
Hi Enric,
On 26/5/23 18:22, eballetbo@kernel.org wrote:
> From: Enric Balletbo i Serra <eballetbo@kernel.org>
>
> This provides a basic infrastructure for the creation of tests for the evdev
> interface. Most of this is code is adapted from the libevdev wrapper
> library. While most of evdev ioctls are covered and tested using
> libevdev tests there are some evdev ioctls that aren't because are not
> supported (and will not be supported) by libevdev. So, adding, at least
> those tests, would make sense.
for the archives: libevdev doesn't support setting EV_REP because it's
inherently racy - one libevdev context to set those values via the ioctl
would cause all other libevdev contexts on the same device to be out of
sync. Since we do not get notifications when the values changed,
libevdev's buffered values for EV_REP will remain whatever they were
initially.
The only way around that would be setting it via udev on plug, but that
udev doesnt need/want the libevdev dependency.
plus, afaik most or all libevdev's users ignore kernel key repeats anyway :)
>
> The test creates an uinput device (and an evdev device) so you can
> call the wanted ioctl from userspace. So, to run those tests you need
> to have support for uinput and evdev as well.
>
> Signed-off-by: Enric Balletbo i Serra <eballetbo@kernel.org>
> ---
> tools/testing/selftests/Makefile | 1 +
> tools/testing/selftests/input/.gitignore | 2 +
> tools/testing/selftests/input/Makefile | 5 +
> tools/testing/selftests/input/config | 3 +
> tools/testing/selftests/input/evioc-test.c | 282 +++++++++++++++++++++
> 5 files changed, 293 insertions(+)
> create mode 100644 tools/testing/selftests/input/.gitignore
> create mode 100644 tools/testing/selftests/input/Makefile
> create mode 100644 tools/testing/selftests/input/config
> create mode 100644 tools/testing/selftests/input/evioc-test.c
>
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index 90a62cf75008..29fc77168aa7 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -28,6 +28,7 @@ TARGETS += futex
> TARGETS += gpio
> TARGETS += hid
> TARGETS += intel_pstate
> +TARGETS += input
> TARGETS += iommu
> TARGETS += ipc
> TARGETS += ir
> diff --git a/tools/testing/selftests/input/.gitignore b/tools/testing/selftests/input/.gitignore
> new file mode 100644
> index 000000000000..37f5dff3255b
> --- /dev/null
> +++ b/tools/testing/selftests/input/.gitignore
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +evioc-test
> diff --git a/tools/testing/selftests/input/Makefile b/tools/testing/selftests/input/Makefile
> new file mode 100644
> index 000000000000..031729be0628
> --- /dev/null
> +++ b/tools/testing/selftests/input/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +CFLAGS += -D_GNU_SOURCE -std=gnu99 -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
> +
> +TEST_GEN_PROGS := evioc-test
> +include ../lib.mk
> diff --git a/tools/testing/selftests/input/config b/tools/testing/selftests/input/config
> new file mode 100644
> index 000000000000..b7512f3e6d8d
> --- /dev/null
> +++ b/tools/testing/selftests/input/config
> @@ -0,0 +1,3 @@
> +CONFIG_INPUT=y
> +CONFIG_INPUT_EVDEV=y
> +CONFIG_INPUT_UINPUT=m
> diff --git a/tools/testing/selftests/input/evioc-test.c b/tools/testing/selftests/input/evioc-test.c
> new file mode 100644
> index 000000000000..4c0c8ebed378
> --- /dev/null
> +++ b/tools/testing/selftests/input/evioc-test.c
> @@ -0,0 +1,282 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2023 Red Hat, Inc.
> + *
> + * Part of the code in this file is inspired and copied from the libevdev wrapper library
> + * for evdev devices written by Peter Hutterer.
> + */
> +
> +#include <dirent.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/uinput.h>
> +#include <poll.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include "../kselftest_harness.h"
> +
> +#define TEST_DEVICE_NAME "selftest input device"
> +
> +struct selftest_uinput {
> + int uinput_fd; /** file descriptor to uinput */
> + int evdev_fd; /** file descriptor to evdev */
> + char *name; /** device name */
> + char *syspath; /** /sys path */
> + char *devnode; /** device node */
> +};
> +
> +static int is_event_device(const struct dirent *dent)
> +{
> + return strncmp("event", dent->d_name, 5) == 0;
> +}
> +
> +static char *fetch_device_node(const char *path)
> +{
> + struct dirent **namelist;
> + char *devnode = NULL;
> + int ndev, i;
> +
> + ndev = scandir(path, &namelist, is_event_device, alphasort);
> + if (ndev <= 0)
> + return NULL;
> +
> + /* ndev should only ever be 1 */
> +
> + for (i = 0; i < ndev; i++) {
> + if (!devnode && asprintf(&devnode, "/dev/input/%s",
> + namelist[i]->d_name) == -1)
> + devnode = NULL;
> + free(namelist[i]);
> + }
> +
> + free(namelist);
> +
> + return devnode;
> +}
> +
> +static int is_input_device(const struct dirent *dent)
> +{
> + return strncmp("input", dent->d_name, 5) == 0;
> +}
> +
> +/*
> + * Device node is guessed based on the sysfs path, the sysfs path contains a
> + * eventN file, that corresponds to our /dev/input/eventN number.
> + */
> +static int fetch_syspath_and_devnode(struct selftest_uinput *uidev)
> +{
> +#define SYS_INPUT_DIR "/sys/devices/virtual/input/"
> + struct dirent **namelist;
> + int ndev, i;
> + int rc;
> + char buf[sizeof(SYS_INPUT_DIR) + 64] = SYS_INPUT_DIR;
> +
> + rc = ioctl(uidev->uinput_fd,
> + UI_GET_SYSNAME(sizeof(buf) - strlen(SYS_INPUT_DIR)),
> + &buf[strlen(SYS_INPUT_DIR)]);
> + if (rc != -1) {
> + uidev->syspath = strdup(buf);
> + uidev->devnode = fetch_device_node(buf);
> + return 0;
> + }
It's not obvious in the the code, but the above is pretty much all you
need. UI_GET_SYSNAME has been around 3.15 and if that fails you might as
well bail out in this case. All the code below is the fallback that
pre-dates that ioctl.
> +
> + ndev = scandir(SYS_INPUT_DIR, &namelist, is_input_device, alphasort);
> + if (ndev <= 0)
> + return -1;
> +
> + for (i = 0; i < ndev; i++) {
> + int fd, len;
> +
> + rc = snprintf(buf, sizeof(buf), "%s%s/name", SYS_INPUT_DIR,
> + namelist[i]->d_name);
> + if (rc < 0 || (size_t)rc >= sizeof(buf))
> + continue;
> +
> + /* created within time frame */
> + fd = open(buf, O_RDONLY);
> + if (fd < 0)
> + continue;
> +
> + len = read(fd, buf, sizeof(buf));
> + close(fd);
> + if (len <= 0)
> + continue;
> +
> + buf[len - 1] = '\0'; /* file contains \n */
> + if (strcmp(buf, uidev->name) == 0) {
> + if (uidev->syspath) {
> + fprintf(stderr,
> + "multiple identical devices found. syspath is unreliable\n");
> + break;
> + }
> +
> + rc = snprintf(buf, sizeof(buf), "%s%s", SYS_INPUT_DIR,
> + namelist[i]->d_name);
> +
> + if (rc < 0 || (size_t)rc >= sizeof(buf)) {
> + fprintf(stderr,
> + "Invalid syspath, syspath is unreliable\n");
> + break;
> + }
> +
> + uidev->syspath = strdup(buf);
> + uidev->devnode = fetch_device_node(buf);
> + }
> + }
> +
> + for (i = 0; i < ndev; i++)
> + free(namelist[i]);
> + free(namelist);
> +
> + return uidev->devnode ? 0 : -1;
> +#undef SYS_INPUT_DIR
> +}
> +
> +void selftest_uinput_destroy(struct selftest_uinput *uidev)
> +{
> + if (!uidev)
> + return;
> +
> + if (uidev->uinput_fd >= 0)
> + ioctl(uidev->uinput_fd, UI_DEV_DESTROY, NULL);
> +
> + close(uidev->evdev_fd);
> + close(uidev->uinput_fd);
> +
> + free(uidev->syspath);
> + free(uidev->devnode);
> + free(uidev->name);
> + free(uidev);
> +}
> +
> +int selftest_uinput_create_device(struct selftest_uinput **uidev, ...)
> +{
> + struct selftest_uinput *new_device;
> + struct uinput_setup setup;
> + va_list args;
> + int rc, fd;
> + int type;
> +
> + new_device = calloc(1, sizeof(struct selftest_uinput));
> + if (!new_device)
> + return -ENOMEM;
> +
> + memset(&setup, 0, sizeof(setup));
> + strncpy(setup.name, TEST_DEVICE_NAME, UINPUT_MAX_NAME_SIZE - 1);
> + setup.id.vendor = 0x1234; /* sample vendor */
> + setup.id.product = 0x5678; /* sample product */
> + setup.id.bustype = BUS_USB;
> +
> + fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
> + if (fd < 0) {
> + fprintf(stderr, "cannot open uinput (%d): %m\n", errno);
> + goto error;
> + }
> +
> + va_start(args, uidev);
> + rc = 0;
> + do {
> + type = va_arg(args, int);
> + if (type == -1)
> + break;
> + rc = ioctl(fd, UI_SET_EVBIT, type);
> + } while (rc == 0);
> + va_end(args);
> +
> + rc = ioctl(fd, UI_DEV_SETUP, &setup);
> + if (rc == -1)
> + goto error;
> +
> + rc = ioctl(fd, UI_DEV_CREATE, NULL);
> + if (rc == -1)
> + goto error;
> +
> + new_device->name = strdup(TEST_DEVICE_NAME);
> + new_device->uinput_fd = fd;
> +
> + if (fetch_syspath_and_devnode(new_device) == -1) {
> + fprintf(stderr, "unable to fetch syspath or device node.\n");
> + errno = ENODEV;
> + goto error;
> + }
> +
> + fd = open(new_device->devnode, O_RDONLY);
> + if (fd < 0) {
> + fprintf(stderr, "cannot open uinput (%d): %m\n", errno);
> + goto error;
> + }
> + new_device->evdev_fd = fd;
> +
> + *uidev = new_device;
> +
> + return 0;
> +
> +error:
> + rc = -errno;
> + selftest_uinput_destroy(new_device);
> + return rc;
> +}
> +
> +const char *selftest_uinput_get_devnode(struct selftest_uinput *uidev)
> +{
> + return uidev->devnode;
> +}
> +
> +TEST(eviocgname_get_device_name)
> +{
> + struct selftest_uinput *uidev;
> + char buf[256];
> + int rc;
> +
> + rc = selftest_uinput_create_device(&uidev);
> + ASSERT_EQ(0, rc);
> + ASSERT_NE(NULL, uidev);
> +
> + memset(buf, 0, sizeof(buf));
> + /* ioctl to get the name */
> + rc = ioctl(uidev->evdev_fd, EVIOCGNAME(sizeof(buf) - 1), buf);
> + ASSERT_GE(rc, 0);
> + ASSERT_STREQ(TEST_DEVICE_NAME, buf);
> +
> + selftest_uinput_destroy(uidev);
> +}
> +
> +TEST(eviocgrep_get_repeat_settings)
> +{
> + struct selftest_uinput *uidev;
> + int rep_values[2];
> + int rc;
> +
> + memset(rep_values, 0, sizeof(rep_values));
> +
> + rc = selftest_uinput_create_device(&uidev);
> + ASSERT_EQ(0, rc);
> + ASSERT_NE(NULL, uidev);
> +
> + /* ioctl to get the repeat rates values */
> + rc = ioctl(uidev->evdev_fd, EVIOCSREP, rep_values);
EVIOCSREP *sets* the repeat values
> + /* should fail because EV_REP is not set */
> + ASSERT_EQ(-1, rc);
> +
> + selftest_uinput_destroy(uidev);
> +
> + rc = selftest_uinput_create_device(&uidev, EV_REP);
This needs a -1 terminator, otherwise you're va-argsing into oblivion.
> + ASSERT_EQ(0, rc);
> + ASSERT_NE(NULL, uidev);
> +
> + /* ioctl to get the repeat rates values */
> + rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values);
> + ASSERT_EQ(0, rc);
> + /* should get the default delay an period values set by the kernel */
typo -> "and"
> + ASSERT_EQ(rep_values[0], 250);
> + ASSERT_EQ(rep_values[1], 33);
So, interestingly enough you don't actually test setting the value here.
The EV_REP bit is set during device creation and you check the default
values but you don't call EVIOCSREP to change the values - which AFAIU
was the initial intention of these tests?
Cheers,
PEter
> +
> + selftest_uinput_destroy(uidev);
> +}
> +
> +TEST_HARNESS_MAIN
^ permalink raw reply
* Re: [PATCH v2 6/7] docs: update some straggling Documentation/arm references
From: Nicolas.Ferre @ 2023-05-30 7:20 UTC (permalink / raw)
To: corbet, linux-doc
Cc: linux-arch, linux-arm-kernel, dmitry.torokhov, wens,
jernej.skrabec, samuel, thierry.reding, u.kleine-koenig, gregkh,
linux-input, linux-sunxi, linux-pwm, linux-serial
In-Reply-To: <20230529144856.102755-7-corbet@lwn.net>
On 29/05/2023 at 16:48, Jonathan Corbet wrote:
> diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
> index 0c567d9623cd..5f7d286871cf 100644
> --- a/drivers/pwm/pwm-atmel.c
> +++ b/drivers/pwm/pwm-atmel.c
> @@ -6,7 +6,7 @@
> * Bo Shen<voice.shen@atmel.com>
> *
> * Links to reference manuals for the supported PWM chips can be found in
> - * Documentation/arm/microchip.rst.
> + * Documentation/arch/arm/microchip.rst.
> *
> * Limitations:
> * - Periods start with the inactive level.
For Microchip changes:
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Thanks Jonathan. Best regards,
Nicolas
--
Nicolas Ferre
^ permalink raw reply
* Re: [PATCH 1/1] HID: logitech-hidpp: Add USB and Bluetooth IDs for the Logitech G915 TKL Keyboard
From: Bastien Nocera @ 2023-05-30 8:36 UTC (permalink / raw)
To: Stuart Hayhurst, jikos
Cc: Filipe Laíns, Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20230529233025.22932-1-stuart.a.hayhurst@gmail.com>
On Tue, 2023-05-30 at 00:30 +0100, Stuart Hayhurst wrote:
> From: stuarthayhurst <stuart.a.hayhurst@gmail.com>
>
> Adds the USB and Bluetooth IDs for the Logitech G915 TKL keyboard
Which functionality gets enabled by that addition, battery reporting,
something else?
>
> ---
> drivers/hid/hid-logitech-hidpp.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-
> logitech-hidpp.c
> index b2cd7527de19..28761272afe5 100644
> --- a/drivers/hid/hid-logitech-hidpp.c
> +++ b/drivers/hid/hid-logitech-hidpp.c
> @@ -4403,6 +4403,8 @@ static const struct hid_device_id
> hidpp_devices[] = {
> HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
> { /* Logitech G903 Hero Gaming Mouse over USB */
> HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) },
> + { /* Logitech G915 TKL Keyboard over USB */
> + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC343) },
> { /* Logitech G920 Wheel over USB */
> HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
> USB_DEVICE_ID_LOGITECH_G920_WHEEL),
> .driver_data = HIDPP_QUIRK_CLASS_G920 |
> HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
> @@ -4418,6 +4420,8 @@ static const struct hid_device_id
> hidpp_devices[] = {
> { /* MX5500 keyboard over Bluetooth */
> HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
> .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
> + { /* Logitech G915 TKL keyboard over Bluetooth */
> + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb35f) },
> { /* M-RCQ142 V470 Cordless Laser Mouse over Bluetooth */
> HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
> { /* MX Master mouse over Bluetooth */
^ permalink raw reply
* Re: [PATCH 2/2] Input: pm8941-powerkey - fix debounce on gen2+ PMICs
From: Konrad Dybcio @ 2023-05-30 9:36 UTC (permalink / raw)
To: Caleb Connolly, Dmitry Torokhov, Andy Gross, Bjorn Andersson
Cc: linux-input, linux-arm-msm, phone-devel
In-Reply-To: <20230529-pm8941-pwrkey-debounce-v1-2-c043a6d5c814@linaro.org>
On 29.05.2023 21:55, Caleb Connolly wrote:
> Since PM8998/PM660, the power key debounce register was redefined to
> support shorter debounce times. On PM8941 the shortest debounce time
> (represented by register value 0) was 15625us, on PM8998 the shortest
> debounce time is 62us, with the default being 2ms.
>
> Adjust the bit shift to correctly program debounce on PM8998 and newer.
>
> Fixes: 68c581d5e7d8 ("Input: add Qualcomm PM8941 power key driver")
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> ---
> This patch shouldn't be backported earlier then 5.4, as that is the
> first kernel with support for PM8998.
> ---
> drivers/input/misc/pm8941-pwrkey.c | 19 +++++++++++++++----
> 1 file changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
> index b6a27ebae977..74d77d8aaeff 100644
> --- a/drivers/input/misc/pm8941-pwrkey.c
> +++ b/drivers/input/misc/pm8941-pwrkey.c
> @@ -50,7 +50,10 @@
> #define PON_RESIN_PULL_UP BIT(0)
>
> #define PON_DBC_CTL 0x71
> -#define PON_DBC_DELAY_MASK 0x7
> +#define PON_DBC_DELAY_MASK_GEN1 0x7
> +#define PON_DBC_DELAY_MASK_GEN2 0xf
> +#define PON_DBC_SHIFT_GEN1 6
> +#define PON_DBC_SHIFT_GEN2 14
mask+shift -> field_prep/get?
Nice find!
Konrad
>
> struct pm8941_data {
> unsigned int pull_up_bit;
> @@ -247,7 +250,7 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
> struct device *parent;
> struct device_node *regmap_node;
> const __be32 *addr;
> - u32 req_delay;
> + u32 req_delay, mask, delay_shift;
> int error;
>
> if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
> @@ -336,12 +339,20 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
> pwrkey->input->phys = pwrkey->data->phys;
>
> if (pwrkey->data->supports_debounce_config) {
> - req_delay = (req_delay << 6) / USEC_PER_SEC;
> + if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY) {
> + mask = PON_DBC_DELAY_MASK_GEN2;
> + delay_shift = PON_DBC_SHIFT_GEN2;
> + } else {
> + mask = PON_DBC_DELAY_MASK_GEN1;
> + delay_shift = PON_DBC_SHIFT_GEN1;
> + }
> +
> + req_delay = (req_delay << delay_shift) / USEC_PER_SEC;
> req_delay = ilog2(req_delay);
>
> error = regmap_update_bits(pwrkey->regmap,
> pwrkey->baseaddr + PON_DBC_CTL,
> - PON_DBC_DELAY_MASK,
> + mask,
> req_delay);
> if (error) {
> dev_err(&pdev->dev, "failed to set debounce: %d\n",
>
^ permalink raw reply
* Re: [PATCH] selftests/input: Introduce basic tests for evdev ioctls
From: Enric Balletbo Serra @ 2023-05-30 10:11 UTC (permalink / raw)
To: Peter Hutterer
Cc: eballetbo, linux-kselftest, linux-kernel, Shuah Khan,
Dmitry Torokhov, Javier Martinez Canillas, Dana Elfassy,
linux-input, phuttere, Benjamin Tissoires
In-Reply-To: <CAFqH_52L+fq_hZaN1xuBFWecNADWG7_TWbebKxmU8hsO9fJe1Q@mail.gmail.com>
Again, without html format. Sorry for the noise.
Hi Peter,
Many thanks for taking a look at this and for the review comments.
> Missatge de Peter Hutterer <peter.hutterer@redhat.com> del dia dt., 30 de maig 2023 a les 7:56:
> >
> > Hi Enric,
> >
> > On 26/5/23 18:22, eballetbo@kernel.org wrote:
> > > From: Enric Balletbo i Serra <eballetbo@kernel.org>
> > >
> > > This provides a basic infrastructure for the creation of tests for the evdev
> > > interface. Most of this is code is adapted from the libevdev wrapper
> > > library. While most of evdev ioctls are covered and tested using
> > > libevdev tests there are some evdev ioctls that aren't because are not
> > > supported (and will not be supported) by libevdev. So, adding, at least
> > > those tests, would make sense.
> >
> > for the archives: libevdev doesn't support setting EV_REP because it's
> > inherently racy - one libevdev context to set those values via the ioctl
> > would cause all other libevdev contexts on the same device to be out of
> > sync. Since we do not get notifications when the values changed,
> > libevdev's buffered values for EV_REP will remain whatever they were
> > initially.
> >
> > The only way around that would be setting it via udev on plug, but that
> > udev doesnt need/want the libevdev dependency.
> >
> > plus, afaik most or all libevdev's users ignore kernel key repeats anyway :)
> >
> > >
> > > The test creates an uinput device (and an evdev device) so you can
> > > call the wanted ioctl from userspace. So, to run those tests you need
> > > to have support for uinput and evdev as well.
> > >
> > > Signed-off-by: Enric Balletbo i Serra <eballetbo@kernel.org>
> > > ---
> > > tools/testing/selftests/Makefile | 1 +
> > > tools/testing/selftests/input/.gitignore | 2 +
> > > tools/testing/selftests/input/Makefile | 5 +
> > > tools/testing/selftests/input/config | 3 +
> > > tools/testing/selftests/input/evioc-test.c | 282 +++++++++++++++++++++
> > > 5 files changed, 293 insertions(+)
> > > create mode 100644 tools/testing/selftests/input/.gitignore
> > > create mode 100644 tools/testing/selftests/input/Makefile
> > > create mode 100644 tools/testing/selftests/input/config
> > > create mode 100644 tools/testing/selftests/input/evioc-test.c
> > >
> > > diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> > > index 90a62cf75008..29fc77168aa7 100644
> > > --- a/tools/testing/selftests/Makefile
> > > +++ b/tools/testing/selftests/Makefile
> > > @@ -28,6 +28,7 @@ TARGETS += futex
> > > TARGETS += gpio
> > > TARGETS += hid
> > > TARGETS += intel_pstate
> > > +TARGETS += input
> > > TARGETS += iommu
> > > TARGETS += ipc
> > > TARGETS += ir
> > > diff --git a/tools/testing/selftests/input/.gitignore b/tools/testing/selftests/input/.gitignore
> > > new file mode 100644
> > > index 000000000000..37f5dff3255b
> > > --- /dev/null
> > > +++ b/tools/testing/selftests/input/.gitignore
> > > @@ -0,0 +1,2 @@
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +evioc-test
> > > diff --git a/tools/testing/selftests/input/Makefile b/tools/testing/selftests/input/Makefile
> > > new file mode 100644
> > > index 000000000000..031729be0628
> > > --- /dev/null
> > > +++ b/tools/testing/selftests/input/Makefile
> > > @@ -0,0 +1,5 @@
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +CFLAGS += -D_GNU_SOURCE -std=gnu99 -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
> > > +
> > > +TEST_GEN_PROGS := evioc-test
> > > +include ../lib.mk
> > > diff --git a/tools/testing/selftests/input/config b/tools/testing/selftests/input/config
> > > new file mode 100644
> > > index 000000000000..b7512f3e6d8d
> > > --- /dev/null
> > > +++ b/tools/testing/selftests/input/config
> > > @@ -0,0 +1,3 @@
> > > +CONFIG_INPUT=y
> > > +CONFIG_INPUT_EVDEV=y
> > > +CONFIG_INPUT_UINPUT=m
> > > diff --git a/tools/testing/selftests/input/evioc-test.c b/tools/testing/selftests/input/evioc-test.c
> > > new file mode 100644
> > > index 000000000000..4c0c8ebed378
> > > --- /dev/null
> > > +++ b/tools/testing/selftests/input/evioc-test.c
> > > @@ -0,0 +1,282 @@
> > > +// SPDX-License-Identifier: MIT
> > > +/*
> > > + * Copyright © 2023 Red Hat, Inc.
> > > + *
> > > + * Part of the code in this file is inspired and copied from the libevdev wrapper library
> > > + * for evdev devices written by Peter Hutterer.
> > > + */
> > > +
> > > +#include <dirent.h>
> > > +#include <errno.h>
> > > +#include <fcntl.h>
> > > +#include <linux/uinput.h>
> > > +#include <poll.h>
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <string.h>
> > > +#include <sys/stat.h>
> > > +#include <time.h>
> > > +#include <unistd.h>
> > > +
> > > +#include "../kselftest_harness.h"
> > > +
> > > +#define TEST_DEVICE_NAME "selftest input device"
> > > +
> > > +struct selftest_uinput {
> > > + int uinput_fd; /** file descriptor to uinput */
> > > + int evdev_fd; /** file descriptor to evdev */
> > > + char *name; /** device name */
> > > + char *syspath; /** /sys path */
> > > + char *devnode; /** device node */
> > > +};
> > > +
> > > +static int is_event_device(const struct dirent *dent)
> > > +{
> > > + return strncmp("event", dent->d_name, 5) == 0;
> > > +}
> > > +
> > > +static char *fetch_device_node(const char *path)
> > > +{
> > > + struct dirent **namelist;
> > > + char *devnode = NULL;
> > > + int ndev, i;
> > > +
> > > + ndev = scandir(path, &namelist, is_event_device, alphasort);
> > > + if (ndev <= 0)
> > > + return NULL;
> > > +
> > > + /* ndev should only ever be 1 */
> > > +
> > > + for (i = 0; i < ndev; i++) {
> > > + if (!devnode && asprintf(&devnode, "/dev/input/%s",
> > > + namelist[i]->d_name) == -1)
> > > + devnode = NULL;
> > > + free(namelist[i]);
> > > + }
> > > +
> > > + free(namelist);
> > > +
> > > + return devnode;
> > > +}
> > > +
> > > +static int is_input_device(const struct dirent *dent)
> > > +{
> > > + return strncmp("input", dent->d_name, 5) == 0;
> > > +}
> > > +
> > > +/*
> > > + * Device node is guessed based on the sysfs path, the sysfs path contains a
> > > + * eventN file, that corresponds to our /dev/input/eventN number.
> > > + */
> > > +static int fetch_syspath_and_devnode(struct selftest_uinput *uidev)
> > > +{
> > > +#define SYS_INPUT_DIR "/sys/devices/virtual/input/"
> > > + struct dirent **namelist;
> > > + int ndev, i;
> > > + int rc;
> > > + char buf[sizeof(SYS_INPUT_DIR) + 64] = SYS_INPUT_DIR;
> > > +
> > > + rc = ioctl(uidev->uinput_fd,
> > > + UI_GET_SYSNAME(sizeof(buf) - strlen(SYS_INPUT_DIR)),
> > > + &buf[strlen(SYS_INPUT_DIR)]);
> > > + if (rc != -1) {
> > > + uidev->syspath = strdup(buf);
> > > + uidev->devnode = fetch_device_node(buf);
> > > + return 0;
> > > + }
> >
> > It's not obvious in the the code, but the above is pretty much all you
> > need. UI_GET_SYSNAME has been around 3.15 and if that fails you might as
> > well bail out in this case. All the code below is the fallback that
> > pre-dates that ioctl.
> >
I see now, thanks. So I think we can simplify all this and make the
assumption that a recent kernel version will be used, so we can remove
the fallback code.
> > > +
> > > + ndev = scandir(SYS_INPUT_DIR, &namelist, is_input_device, alphasort);
> > > + if (ndev <= 0)
> > > + return -1;
> > > +
> > > + for (i = 0; i < ndev; i++) {
> > > + int fd, len;
> > > +
> > > + rc = snprintf(buf, sizeof(buf), "%s%s/name", SYS_INPUT_DIR,
> > > + namelist[i]->d_name);
> > > + if (rc < 0 || (size_t)rc >= sizeof(buf))
> > > + continue;
> > > +
> > > + /* created within time frame */
> > > + fd = open(buf, O_RDONLY);
> > > + if (fd < 0)
> > > + continue;
> > > +
> > > + len = read(fd, buf, sizeof(buf));
> > > + close(fd);
> > > + if (len <= 0)
> > > + continue;
> > > +
> > > + buf[len - 1] = '\0'; /* file contains \n */
> > > + if (strcmp(buf, uidev->name) == 0) {
> > > + if (uidev->syspath) {
> > > + fprintf(stderr,
> > > + "multiple identical devices found. syspath is unreliable\n");
> > > + break;
> > > + }
> > > +
> > > + rc = snprintf(buf, sizeof(buf), "%s%s", SYS_INPUT_DIR,
> > > + namelist[i]->d_name);
> > > +
> > > + if (rc < 0 || (size_t)rc >= sizeof(buf)) {
> > > + fprintf(stderr,
> > > + "Invalid syspath, syspath is unreliable\n");
> > > + break;
> > > + }
> > > +
> > > + uidev->syspath = strdup(buf);
> > > + uidev->devnode = fetch_device_node(buf);
> > > + }
> > > + }
> > > +
> > > + for (i = 0; i < ndev; i++)
> > > + free(namelist[i]);
> > > + free(namelist);
> > > +
> > > + return uidev->devnode ? 0 : -1;
> > > +#undef SYS_INPUT_DIR
> > > +}
> > > +
> > > +void selftest_uinput_destroy(struct selftest_uinput *uidev)
> > > +{
> > > + if (!uidev)
> > > + return;
> > > +
> > > + if (uidev->uinput_fd >= 0)
> > > + ioctl(uidev->uinput_fd, UI_DEV_DESTROY, NULL);
> > > +
> > > + close(uidev->evdev_fd);
> > > + close(uidev->uinput_fd);
> > > +
> > > + free(uidev->syspath);
> > > + free(uidev->devnode);
> > > + free(uidev->name);
> > > + free(uidev);
> > > +}
> > > +
> > > +int selftest_uinput_create_device(struct selftest_uinput **uidev, ...)
> > > +{
> > > + struct selftest_uinput *new_device;
> > > + struct uinput_setup setup;
> > > + va_list args;
> > > + int rc, fd;
> > > + int type;
> > > +
> > > + new_device = calloc(1, sizeof(struct selftest_uinput));
> > > + if (!new_device)
> > > + return -ENOMEM;
> > > +
> > > + memset(&setup, 0, sizeof(setup));
> > > + strncpy(setup.name, TEST_DEVICE_NAME, UINPUT_MAX_NAME_SIZE - 1);
> > > + setup.id.vendor = 0x1234; /* sample vendor */
> > > + setup.id.product = 0x5678; /* sample product */
> > > + setup.id.bustype = BUS_USB;
> > > +
> > > + fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
> > > + if (fd < 0) {
> > > + fprintf(stderr, "cannot open uinput (%d): %m\n", errno);
> > > + goto error;
> > > + }
> > > +
> > > + va_start(args, uidev);
> > > + rc = 0;
> > > + do {
> > > + type = va_arg(args, int);
> > > + if (type == -1)
> > > + break;
> > > + rc = ioctl(fd, UI_SET_EVBIT, type);
> > > + } while (rc == 0);
> > > + va_end(args);
> > > +
> > > + rc = ioctl(fd, UI_DEV_SETUP, &setup);
> > > + if (rc == -1)
> > > + goto error;
> > > +
> > > + rc = ioctl(fd, UI_DEV_CREATE, NULL);
> > > + if (rc == -1)
> > > + goto error;
> > > +
> > > + new_device->name = strdup(TEST_DEVICE_NAME);
> > > + new_device->uinput_fd = fd;
> > > +
> > > + if (fetch_syspath_and_devnode(new_device) == -1) {
> > > + fprintf(stderr, "unable to fetch syspath or device node.\n");
> > > + errno = ENODEV;
> > > + goto error;
> > > + }
> > > +
> > > + fd = open(new_device->devnode, O_RDONLY);
> > > + if (fd < 0) {
> > > + fprintf(stderr, "cannot open uinput (%d): %m\n", errno);
> > > + goto error;
> > > + }
> > > + new_device->evdev_fd = fd;
> > > +
> > > + *uidev = new_device;
> > > +
> > > + return 0;
> > > +
> > > +error:
> > > + rc = -errno;
> > > + selftest_uinput_destroy(new_device);
> > > + return rc;
> > > +}
> > > +
> > > +const char *selftest_uinput_get_devnode(struct selftest_uinput *uidev)
> > > +{
> > > + return uidev->devnode;
> > > +}
> > > +
> > > +TEST(eviocgname_get_device_name)
> > > +{
> > > + struct selftest_uinput *uidev;
> > > + char buf[256];
> > > + int rc;
> > > +
> > > + rc = selftest_uinput_create_device(&uidev);
> > > + ASSERT_EQ(0, rc);
> > > + ASSERT_NE(NULL, uidev);
> > > +
> > > + memset(buf, 0, sizeof(buf));
> > > + /* ioctl to get the name */
> > > + rc = ioctl(uidev->evdev_fd, EVIOCGNAME(sizeof(buf) - 1), buf);
> > > + ASSERT_GE(rc, 0);
> > > + ASSERT_STREQ(TEST_DEVICE_NAME, buf);
> > > +
> > > + selftest_uinput_destroy(uidev);
> > > +}
> > > +
> > > +TEST(eviocgrep_get_repeat_settings)
> > > +{
> > > + struct selftest_uinput *uidev;
> > > + int rep_values[2];
> > > + int rc;
> > > +
> > > + memset(rep_values, 0, sizeof(rep_values));
> > > +
> > > + rc = selftest_uinput_create_device(&uidev);
> > > + ASSERT_EQ(0, rc);
> > > + ASSERT_NE(NULL, uidev);
> > > +
> > > + /* ioctl to get the repeat rates values */
> > > + rc = ioctl(uidev->evdev_fd, EVIOCSREP, rep_values);
> >
> > EVIOCSREP *sets* the repeat values
My intention was to use EVIOCGREP here instead of 'S', so instead of
fixing the comment I'd fix the ioctl. Thanks.
> >
> >
> > > + /* should fail because EV_REP is not set */
> > > + ASSERT_EQ(-1, rc);
> > > +
> > > + selftest_uinput_destroy(uidev);
> > > +
> > > + rc = selftest_uinput_create_device(&uidev, EV_REP);
> > This needs a -1 terminator, otherwise you're va-argsing into oblivion.
> >
Right, I'd add in the next version.
> >
> > > + ASSERT_EQ(0, rc);
> > > + ASSERT_NE(NULL, uidev);
> > > +
> > > + /* ioctl to get the repeat rates values */
> > > + rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values);
> > > + ASSERT_EQ(0, rc);
> > > + /* should get the default delay an period values set by the kernel */
> >
> > typo -> "and"
> >
Fixed in the next version.
> > > + ASSERT_EQ(rep_values[0], 250);
> > > + ASSERT_EQ(rep_values[1], 33);
> >
> > So, interestingly enough you don't actually test setting the value here.
> > The EV_REP bit is set during device creation and you check the default
> > values but you don't call EVIOCSREP to change the values - which AFAIU
> > was the initial intention of these tests?
> >
>
Yes, one of the main purposes is to test EVIOC**S**REP, but I skipped
and sent a first version only testing the EVIOC**G**REP test to catch
feedback from the mailing list. In the next version I'll include both
tests, for getting and setting the repeat values (I think it makes
sense).
Cheers,
Enric
>
> > Cheers,
> > PEter
> >
> > > +
> > > + selftest_uinput_destroy(uidev);
> > > +}
> > > +
> > > +TEST_HARNESS_MAIN
^ permalink raw reply
* [PATCH v2] selftests/input: Introduce basic tests for evdev ioctls
From: Enric Balletbo i Serra @ 2023-05-30 10:26 UTC (permalink / raw)
To: linux-kselftest, linux-kernel
Cc: Shuah Khan, Dmitry Torokhov, Javier Martinez Canillas,
Dana Elfassy, linux-input, phuttere, Benjamin Tissoires,
Enric Balletbo i Serra
This provides a basic infrastructure for the creation of tests for the evdev
interface. Most of this code is adapted from the libevdev wrapper library. While
most of evdev ioctls are covered and tested using libevdev tests there are some
evdev ioctls that aren't because are not supported (and will not be supported)
by libevdev [1]. So, adding, at least those tests, would make sense.
The test creates an uinput device (and an evdev device) so you can
call the wanted ioctl from userspace. So, to run those tests you need
to have support for uinput and evdev as well.
[1] For example, libevdev doesn't support setting EV_REP because it's inherently
racy - one libevdev context to set those values via the ioctl would cause all
other libevdev contexts on the same device to be out of sync. Since we do not
get notifications when the values changed, libevdev's buffered values for EV_REP
will remain whatever they were initially.
Signed-off-by: Enric Balletbo i Serra <eballetbo@kernel.org>
---
Test output:
TAP version 13
1..3
# Starting 3 tests from 1 test cases.
# RUN global.eviocgname_get_device_name ...
# OK global.eviocgname_get_device_name
ok 1 global.eviocgname_get_device_name
# RUN global.eviocgrep_get_repeat_settings ...
# OK global.eviocgrep_get_repeat_settings
ok 2 global.eviocgrep_get_repeat_settings
# RUN global.eviocsrep_set_repeat_settings ...
# OK global.eviocsrep_set_repeat_settings
ok 3 global.eviocsrep_set_repeat_settings
# PASSED: 3 / 3 tests passed.
# Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/input/.gitignore | 2 +
tools/testing/selftests/input/Makefile | 5 +
tools/testing/selftests/input/config | 3 +
tools/testing/selftests/input/evioc-test.c | 259 +++++++++++++++++++++
5 files changed, 270 insertions(+)
create mode 100644 tools/testing/selftests/input/.gitignore
create mode 100644 tools/testing/selftests/input/Makefile
create mode 100644 tools/testing/selftests/input/config
create mode 100644 tools/testing/selftests/input/evioc-test.c
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 90a62cf75008..29fc77168aa7 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -28,6 +28,7 @@ TARGETS += futex
TARGETS += gpio
TARGETS += hid
TARGETS += intel_pstate
+TARGETS += input
TARGETS += iommu
TARGETS += ipc
TARGETS += ir
diff --git a/tools/testing/selftests/input/.gitignore b/tools/testing/selftests/input/.gitignore
new file mode 100644
index 000000000000..37f5dff3255b
--- /dev/null
+++ b/tools/testing/selftests/input/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+evioc-test
diff --git a/tools/testing/selftests/input/Makefile b/tools/testing/selftests/input/Makefile
new file mode 100644
index 000000000000..031729be0628
--- /dev/null
+++ b/tools/testing/selftests/input/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+CFLAGS += -D_GNU_SOURCE -std=gnu99 -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
+
+TEST_GEN_PROGS := evioc-test
+include ../lib.mk
diff --git a/tools/testing/selftests/input/config b/tools/testing/selftests/input/config
new file mode 100644
index 000000000000..b7512f3e6d8d
--- /dev/null
+++ b/tools/testing/selftests/input/config
@@ -0,0 +1,3 @@
+CONFIG_INPUT=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_UINPUT=m
diff --git a/tools/testing/selftests/input/evioc-test.c b/tools/testing/selftests/input/evioc-test.c
new file mode 100644
index 000000000000..1b951d41ee47
--- /dev/null
+++ b/tools/testing/selftests/input/evioc-test.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023 Red Hat, Inc.
+ *
+ * Part of the code in this file is inspired and copied from the libevdev wrapper library
+ * for evdev devices written by Peter Hutterer.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/uinput.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define TEST_DEVICE_NAME "selftest input device"
+
+struct selftest_uinput {
+ int uinput_fd; /** file descriptor to uinput */
+ int evdev_fd; /** file descriptor to evdev */
+ char *name; /** device name */
+ char *syspath; /** /sys path */
+ char *devnode; /** device node */
+};
+
+static int is_event_device(const struct dirent *dent)
+{
+ return strncmp("event", dent->d_name, 5) == 0;
+}
+
+static char *fetch_device_node(const char *path)
+{
+ struct dirent **namelist;
+ char *devnode = NULL;
+ int ndev, i;
+
+ ndev = scandir(path, &namelist, is_event_device, alphasort);
+ if (ndev <= 0)
+ return NULL;
+
+ /* ndev should only ever be 1 */
+
+ for (i = 0; i < ndev; i++) {
+ if (!devnode && asprintf(&devnode, "/dev/input/%s",
+ namelist[i]->d_name) == -1)
+ devnode = NULL;
+ free(namelist[i]);
+ }
+
+ free(namelist);
+
+ return devnode;
+}
+
+static int fetch_syspath_and_devnode(struct selftest_uinput *uidev)
+{
+#define SYS_INPUT_DIR "/sys/devices/virtual/input/"
+ char buf[sizeof(SYS_INPUT_DIR) + 64] = SYS_INPUT_DIR;
+ int rc;
+
+ rc = ioctl(uidev->uinput_fd,
+ UI_GET_SYSNAME(sizeof(buf) - strlen(SYS_INPUT_DIR)),
+ &buf[strlen(SYS_INPUT_DIR)]);
+ if (rc == -1) {
+ fprintf(stderr, "cannot get the sysfs name of the uinput device (%d)\n", rc);
+ return rc;
+ }
+
+ uidev->syspath = strdup(buf);
+ uidev->devnode = fetch_device_node(buf);
+
+ return uidev->devnode ? 0 : -1;
+#undef SYS_INPUT_DIR
+}
+
+static void selftest_uinput_destroy(struct selftest_uinput *uidev)
+{
+ if (!uidev)
+ return;
+
+ if (uidev->uinput_fd >= 0)
+ ioctl(uidev->uinput_fd, UI_DEV_DESTROY, NULL);
+
+ close(uidev->evdev_fd);
+ close(uidev->uinput_fd);
+
+ free(uidev->syspath);
+ free(uidev->devnode);
+ free(uidev->name);
+ free(uidev);
+}
+
+static int selftest_uinput_create_device(struct selftest_uinput **uidev, ...)
+{
+ struct selftest_uinput *new_device;
+ struct uinput_setup setup;
+ va_list args;
+ int rc, fd;
+ int type;
+
+ new_device = calloc(1, sizeof(struct selftest_uinput));
+ if (!new_device)
+ return -ENOMEM;
+
+ memset(&setup, 0, sizeof(setup));
+ strncpy(setup.name, TEST_DEVICE_NAME, UINPUT_MAX_NAME_SIZE - 1);
+ setup.id.vendor = 0x1234; /* sample vendor */
+ setup.id.product = 0x5678; /* sample product */
+ setup.id.bustype = BUS_USB;
+
+ fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open uinput (%d): %m\n", errno);
+ goto error;
+ }
+
+ va_start(args, uidev);
+ rc = 0;
+ do {
+ type = va_arg(args, int);
+ if (type == -1)
+ break;
+ rc = ioctl(fd, UI_SET_EVBIT, type);
+ } while (rc == 0);
+ va_end(args);
+
+ rc = ioctl(fd, UI_DEV_SETUP, &setup);
+ if (rc == -1)
+ goto error;
+
+ rc = ioctl(fd, UI_DEV_CREATE, NULL);
+ if (rc == -1)
+ goto error;
+
+ new_device->name = strdup(TEST_DEVICE_NAME);
+ new_device->uinput_fd = fd;
+
+ if (fetch_syspath_and_devnode(new_device) == -1) {
+ fprintf(stderr, "unable to fetch syspath or device node.\n");
+ errno = ENODEV;
+ goto error;
+ }
+
+ fd = open(new_device->devnode, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open uinput (%d): %m\n", errno);
+ goto error;
+ }
+ new_device->evdev_fd = fd;
+
+ *uidev = new_device;
+
+ return 0;
+
+error:
+ rc = -errno;
+ selftest_uinput_destroy(new_device);
+ return rc;
+}
+
+TEST(eviocgname_get_device_name)
+{
+ struct selftest_uinput *uidev;
+ char buf[256];
+ int rc;
+
+ rc = selftest_uinput_create_device(&uidev);
+ ASSERT_EQ(0, rc);
+ ASSERT_NE(NULL, uidev);
+
+ memset(buf, 0, sizeof(buf));
+ /* ioctl to get the name */
+ rc = ioctl(uidev->evdev_fd, EVIOCGNAME(sizeof(buf) - 1), buf);
+ ASSERT_GE(rc, 0);
+ ASSERT_STREQ(TEST_DEVICE_NAME, buf);
+
+ selftest_uinput_destroy(uidev);
+}
+
+TEST(eviocgrep_get_repeat_settings)
+{
+ struct selftest_uinput *uidev;
+ int rep_values[2];
+ int rc;
+
+ memset(rep_values, 0, sizeof(rep_values));
+
+ rc = selftest_uinput_create_device(&uidev);
+ ASSERT_EQ(0, rc);
+ ASSERT_NE(NULL, uidev);
+
+ /* ioctl to get the repeat rates values */
+ rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values);
+ /* should fail because EV_REP is not set */
+ ASSERT_EQ(-1, rc);
+
+ selftest_uinput_destroy(uidev);
+
+ rc = selftest_uinput_create_device(&uidev, EV_REP, -1);
+ ASSERT_EQ(0, rc);
+ ASSERT_NE(NULL, uidev);
+
+ /* ioctl to get the repeat rates values */
+ rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values);
+ ASSERT_EQ(0, rc);
+ /* should get the default delay and period values set by the kernel */
+ ASSERT_EQ(rep_values[0], 250);
+ ASSERT_EQ(rep_values[1], 33);
+
+ selftest_uinput_destroy(uidev);
+}
+
+TEST(eviocsrep_set_repeat_settings)
+{
+ struct selftest_uinput *uidev;
+ int rep_values[2];
+ int rc;
+
+ memset(rep_values, 0, sizeof(rep_values));
+
+ rc = selftest_uinput_create_device(&uidev);
+ ASSERT_EQ(0, rc);
+ ASSERT_NE(NULL, uidev);
+
+ /* ioctl to set the repeat rates values */
+ rc = ioctl(uidev->evdev_fd, EVIOCSREP, rep_values);
+ /* should fail because EV_REP is not set */
+ ASSERT_EQ(-1, rc);
+
+ selftest_uinput_destroy(uidev);
+
+ rc = selftest_uinput_create_device(&uidev, EV_REP, -1);
+ ASSERT_EQ(0, rc);
+ ASSERT_NE(NULL, uidev);
+
+ /* ioctl to set the repeat rates values */
+ rep_values[0] = 500;
+ rep_values[1] = 200;
+ rc = ioctl(uidev->evdev_fd, EVIOCSREP, rep_values);
+ ASSERT_EQ(0, rc);
+
+ /* ioctl to get the repeat rates values */
+ rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values);
+ ASSERT_EQ(0, rc);
+ /* should get the delay and period values set previously */
+ ASSERT_EQ(rep_values[0], 500);
+ ASSERT_EQ(rep_values[1], 200);
+
+ selftest_uinput_destroy(uidev);
+}
+
+TEST_HARNESS_MAIN
--
2.40.1
^ permalink raw reply related
* Re: [PATCH] hid-wiimote-debug.c: Fix error checking for debugfs_create_file
From: David Rheinsberg @ 2023-05-30 10:48 UTC (permalink / raw)
To: Osama Muhammad; +Cc: benjamin.tissoires, linux-input, linux-kernel
In-Reply-To: <20230523171240.12930-1-osmtendev@gmail.com>
Hi
On Tue, 23 May 2023 at 19:13, Osama Muhammad <osmtendev@gmail.com> wrote:
>
> This patch fixes the error checking in hid-wiimote-debug.c in
> debugfs_create_file. The correct way to check if an error occurred
> is 'IS_ERR' inline function.
Please have a look at fs/debugfs/inode.c, especiall the docs of
debugfs_create_file():
477 * NOTE: it's expected that most callers should _ignore_ the errors returned
478 * by this function. Other debugfs functions handle the fact that
the "dentry"
479 * passed to them could be an error and they don't crash in that case.
480 * Drivers should generally work fine even if debugfs fails to init anyway.
I think the current code is fine. If you feel like adjusting it, I
would rather just remove the NULL-check.
Thanks
David
> Signed-off-by: Osama Muhammad <osmtendev@gmail.com>
> ---
> drivers/hid/hid-wiimote-debug.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c
> index a99dcca2e099..eddd981fee1a 100644
> --- a/drivers/hid/hid-wiimote-debug.c
> +++ b/drivers/hid/hid-wiimote-debug.c
> @@ -183,12 +183,12 @@ int wiidebug_init(struct wiimote_data *wdata)
>
> dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR,
> dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops);
> - if (!dbg->eeprom)
> + if (IS_ERR(dbg->eeprom))
> goto err;
>
> dbg->drm = debugfs_create_file("drm", S_IRUSR,
> dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops);
> - if (!dbg->drm)
> + if (IS_ERR(dbg->drm))
> goto err_drm;
>
> spin_lock_irqsave(&wdata->state.lock, flags);
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH] hid-wiimote-debug.c: Fix error checking for debugfs_create_file
From: Osama Muhammad @ 2023-05-30 11:04 UTC (permalink / raw)
To: David Rheinsberg; +Cc: benjamin.tissoires, linux-input, linux-kernel
In-Reply-To: <CADyDSO7Lchnraz8pZJkgDBg=YiEG51kcSd+_yg55GpJOuHk6wg@mail.gmail.com>
Hi,
I will adjust it by sending the revision of the patch by removing NULL-check.
Thanks,
Osmten
On Tue, 30 May 2023 at 15:48, David Rheinsberg
<david.rheinsberg@gmail.com> wrote:
>
> Hi
>
> On Tue, 23 May 2023 at 19:13, Osama Muhammad <osmtendev@gmail.com> wrote:
> >
> > This patch fixes the error checking in hid-wiimote-debug.c in
> > debugfs_create_file. The correct way to check if an error occurred
> > is 'IS_ERR' inline function.
>
> Please have a look at fs/debugfs/inode.c, especiall the docs of
> debugfs_create_file():
>
> 477 * NOTE: it's expected that most callers should _ignore_ the errors returned
> 478 * by this function. Other debugfs functions handle the fact that
> the "dentry"
> 479 * passed to them could be an error and they don't crash in that case.
> 480 * Drivers should generally work fine even if debugfs fails to init anyway.
>
> I think the current code is fine. If you feel like adjusting it, I
> would rather just remove the NULL-check.
>
> Thanks
> David
>
> > Signed-off-by: Osama Muhammad <osmtendev@gmail.com>
> > ---
> > drivers/hid/hid-wiimote-debug.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c
> > index a99dcca2e099..eddd981fee1a 100644
> > --- a/drivers/hid/hid-wiimote-debug.c
> > +++ b/drivers/hid/hid-wiimote-debug.c
> > @@ -183,12 +183,12 @@ int wiidebug_init(struct wiimote_data *wdata)
> >
> > dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR,
> > dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops);
> > - if (!dbg->eeprom)
> > + if (IS_ERR(dbg->eeprom))
> > goto err;
> >
> > dbg->drm = debugfs_create_file("drm", S_IRUSR,
> > dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops);
> > - if (!dbg->drm)
> > + if (IS_ERR(dbg->drm))
> > goto err_drm;
> >
> > spin_lock_irqsave(&wdata->state.lock, flags);
> > --
> > 2.34.1
> >
^ permalink raw reply
* [PATCH] input: Add new keyboard backlight control keys to match modern notebooks
From: Werner Sembach @ 2023-05-30 11:05 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Werner Sembach, linux-input, linux-kernel
The old three KEY_KBDILLUM* keycodes don't reflect the current situation
modern notebooks anymore. Especially the ones with RGB keyboards.
e.g.
- Clevo NL50NU has a toggle, an up, a down and a color-cycle key
- TongFang PH4ARX1 doesn't have a toggle key, but one that cycles through
off, half-brightness, and full-brightness.
Also, on some devices these keys are already implemented in firmware. It
would still be nice if there is a way to let userspace know when one of
these keys is pressed to display the OSD, but don't advice it to actually
do anything. This is the intended purpose of the KEY_KBDILLUMCHANGE define.
Signed-off-by: Werner Sembach <wse@tuxedocomputers.com>
---
include/uapi/linux/input-event-codes.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 022a520e31fc2..05287bf9a77f7 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -803,6 +803,10 @@
#define BTN_TRIGGER_HAPPY39 0x2e6
#define BTN_TRIGGER_HAPPY40 0x2e7
+#define KEY_KBDILLUMCYCLE 0x2e8
+#define KEY_KBDILLUMCOLORCYCLE 0x2e9
+#define KEY_KBDILLUMCHANGE 0x2ea
+
/* We avoid low common keys in module aliases so they don't get huge. */
#define KEY_MIN_INTERESTING KEY_MUTE
#define KEY_MAX 0x2ff
--
2.34.1
^ permalink raw reply related
* Re: [v2 2/2] dt-bindings: input: touchscreen: Add ilitek 9882T touchscreen chip
From: Krzysztof Kozlowski @ 2023-05-30 11:56 UTC (permalink / raw)
To: Doug Anderson, Cong Yang
Cc: benjamin.tissoires, devicetree, dmitry.torokhov, hsinyi, jikos,
linux-input, linux-kernel, mka
In-Reply-To: <CAD=FV=Wm_SK0V6WJUkuvu8yFfiP60JBuOdw9cy=0Ck2Jbn-X2A@mail.gmail.com>
On 22/05/2023 17:33, Doug Anderson wrote:
> Hi,
>
> On Fri, May 19, 2023 at 10:07 PM Cong Yang
> <yangcong5@huaqin.corp-partner.google.com> wrote:
>>
>> Add an ilitek touch screen chip ili9882t.
>>
>> Signed-off-by: Cong Yang <yangcong5@huaqin.corp-partner.google.com>
>> ---
>> .../devicetree/bindings/input/elan,ekth6915.yaml | 9 +++++++--
>> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> I'm curious about the DT maintainers opinion here. Should this be a
> new bindings file, or should it be together in the elan file. If
> nothing else, I think the secondary voltage rail name is wrong. I took
> a quick peek at a datasheet I found and I don't even see a 3.3V rail
> going to the ili9882t. That makes it weird to reuse "vcc33-supply" for
> a second supply...
It's easier if they are CCed...
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 1/1] HID: logitech-hidpp: Add USB and Bluetooth IDs for the Logitech G915 TKL Keyboard
From: Stuart @ 2023-05-30 12:52 UTC (permalink / raw)
To: Bastien Nocera
Cc: jikos, Filipe Laíns, Benjamin Tissoires, linux-input,
linux-kernel
In-Reply-To: <468317007b2bcbcce28778f9a1d3ae0426563522.camel@hadess.net>
Using https://github.com/cvuchener/hidpp, the keyboard now seems to
report the following:
Logitech G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard
(046d:c343) is a HID++ 4.2 device
Feature 0x01: [0x0001] Feature set
Feature 0x02: [0x0003] Device FW version
Feature 0x03: [0x0005] Device name
Feature 0x04: [0x1d4b] Wireless device status
Feature 0x05: [0x0020] Reset
Feature 0x06: [0x1001] Battery voltage
Feature 0x07: [0x1814] Change host
Feature 0x08: [0x1815] Hosts info
Feature 0x09: [0x8071] RGB Effects
Feature 0x0a: [0x8081] Per-key lighting v2
Feature 0x0b: [0x1b04] Reprog controls v4
Feature 0x0c: [0x1bc0] Persistent remappable action
Feature 0x0d: [0x4100] Encryption
Feature 0x0e: [0x4522] Disable keys by usage
Feature 0x0f: [0x4540] Keyboard layout 2
Feature 0x10: [0x8010] G-key
Feature 0x11: [0x8020] M-keys
Feature 0x12: [0x8030] MR
Feature 0x13: [0x8040] Brightness control
Feature 0x14: [0x8100] Onboard profiles
Feature 0x15: [0x8060] Report rate
Feature 0x16: [0x00c2] DFUcontrol 3
Feature 0x17: [0x00d0] DFU
Feature 0x18: [0x1802] Device reset (hidden, internal)
Feature 0x19: [0x1803] ? (hidden, internal)
Feature 0x1a: [0x1806] Configurable device properties (hidden, internal)
Feature 0x1b: [0x1813] ? (hidden, internal)
Feature 0x1c: [0x1805] OOBState (hidden, internal)
Feature 0x1d: [0x1830] ? (hidden, internal)
Feature 0x1e: [0x1890] ? (hidden, internal)
Feature 0x1f: [0x1891] ? (hidden, internal)
Feature 0x20: [0x18a1] ? (hidden, internal)
Feature 0x21: [0x1e00] Enable hidden features (hidden)
Feature 0x22: [0x1eb0] ? (hidden, internal)
Feature 0x23: [0x1861] ? (hidden, internal)
Feature 0x24: [0x18b0] ? (hidden, internal)
On Tue, May 30, 2023 at 9:36 AM Bastien Nocera <hadess@hadess.net> wrote:
>
> On Tue, 2023-05-30 at 00:30 +0100, Stuart Hayhurst wrote:
> > From: stuarthayhurst <stuart.a.hayhurst@gmail.com>
> >
> > Adds the USB and Bluetooth IDs for the Logitech G915 TKL keyboard
>
> Which functionality gets enabled by that addition, battery reporting,
> something else?
>
> >
> > ---
> > drivers/hid/hid-logitech-hidpp.c | 4 ++++
> > 1 file changed, 4 insertions(+)
> >
> > diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-
> > logitech-hidpp.c
> > index b2cd7527de19..28761272afe5 100644
> > --- a/drivers/hid/hid-logitech-hidpp.c
> > +++ b/drivers/hid/hid-logitech-hidpp.c
> > @@ -4403,6 +4403,8 @@ static const struct hid_device_id
> > hidpp_devices[] = {
> > HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
> > { /* Logitech G903 Hero Gaming Mouse over USB */
> > HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) },
> > + { /* Logitech G915 TKL Keyboard over USB */
> > + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC343) },
> > { /* Logitech G920 Wheel over USB */
> > HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
> > USB_DEVICE_ID_LOGITECH_G920_WHEEL),
> > .driver_data = HIDPP_QUIRK_CLASS_G920 |
> > HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
> > @@ -4418,6 +4420,8 @@ static const struct hid_device_id
> > hidpp_devices[] = {
> > { /* MX5500 keyboard over Bluetooth */
> > HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
> > .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
> > + { /* Logitech G915 TKL keyboard over Bluetooth */
> > + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb35f) },
> > { /* M-RCQ142 V470 Cordless Laser Mouse over Bluetooth */
> > HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
> > { /* MX Master mouse over Bluetooth */
>
^ permalink raw reply
* Re: [PATCH 2/2] Input: pm8941-powerkey - fix debounce on gen2+ PMICs
From: Caleb Connolly @ 2023-05-30 13:00 UTC (permalink / raw)
To: Konrad Dybcio, Dmitry Torokhov, Andy Gross, Bjorn Andersson
Cc: linux-input, linux-arm-msm, phone-devel
In-Reply-To: <d3e6a788-ecab-dd12-f091-279157ea3ad5@linaro.org>
On 30/05/2023 10:36, Konrad Dybcio wrote:
>
>
> On 29.05.2023 21:55, Caleb Connolly wrote:
>> Since PM8998/PM660, the power key debounce register was redefined to
>> support shorter debounce times. On PM8941 the shortest debounce time
>> (represented by register value 0) was 15625us, on PM8998 the shortest
>> debounce time is 62us, with the default being 2ms.
>>
>> Adjust the bit shift to correctly program debounce on PM8998 and newer.
>>
>> Fixes: 68c581d5e7d8 ("Input: add Qualcomm PM8941 power key driver")
>> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
>> ---
>> This patch shouldn't be backported earlier then 5.4, as that is the
>> first kernel with support for PM8998.
>> ---
>> drivers/input/misc/pm8941-pwrkey.c | 19 +++++++++++++++----
>> 1 file changed, 15 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
>> index b6a27ebae977..74d77d8aaeff 100644
>> --- a/drivers/input/misc/pm8941-pwrkey.c
>> +++ b/drivers/input/misc/pm8941-pwrkey.c
>> @@ -50,7 +50,10 @@
>> #define PON_RESIN_PULL_UP BIT(0)
>>
>> #define PON_DBC_CTL 0x71
>> -#define PON_DBC_DELAY_MASK 0x7
>> +#define PON_DBC_DELAY_MASK_GEN1 0x7
>> +#define PON_DBC_DELAY_MASK_GEN2 0xf
>> +#define PON_DBC_SHIFT_GEN1 6
>> +#define PON_DBC_SHIFT_GEN2 14
> mask+shift -> field_prep/get?
I figured it was better to keep it consistent and try to minimise the
diff so that backporting is easier.
Migrating over to bitfield helpers probably makes sense to do as a
separate change - maybe if a new platform comes along and requires even
more additional complexity?
>
> Nice find!
>
> Konrad
>>
>> struct pm8941_data {
>> unsigned int pull_up_bit;
>> @@ -247,7 +250,7 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>> struct device *parent;
>> struct device_node *regmap_node;
>> const __be32 *addr;
>> - u32 req_delay;
>> + u32 req_delay, mask, delay_shift;
>> int error;
>>
>> if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
>> @@ -336,12 +339,20 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>> pwrkey->input->phys = pwrkey->data->phys;
>>
>> if (pwrkey->data->supports_debounce_config) {
>> - req_delay = (req_delay << 6) / USEC_PER_SEC;
>> + if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY) {
>> + mask = PON_DBC_DELAY_MASK_GEN2;
>> + delay_shift = PON_DBC_SHIFT_GEN2;
>> + } else {
>> + mask = PON_DBC_DELAY_MASK_GEN1;
>> + delay_shift = PON_DBC_SHIFT_GEN1;
>> + }
>> +
>> + req_delay = (req_delay << delay_shift) / USEC_PER_SEC;
>> req_delay = ilog2(req_delay);
>>
>> error = regmap_update_bits(pwrkey->regmap,
>> pwrkey->baseaddr + PON_DBC_CTL,
>> - PON_DBC_DELAY_MASK,
>> + mask,
>> req_delay);
>> if (error) {
>> dev_err(&pdev->dev, "failed to set debounce: %d\n",
>>
--
// Caleb (they/them)
^ permalink raw reply
* [PATCH v2] Input: atkbd - allow skipping the check of keyboard ID when probing
From: Shang Ye @ 2023-05-30 13:13 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Shang Ye, Bernhard Kaindl, linux-input, linux-kernel
There have been several reports about keyboard not working on 9 types of
Lenovo Yoga / XiaoXinPro / IdeaPad (14", Intel) laptops. Here is a dmesg
log that illustrates the problem on a Yoga 14sIHU 2021:
https://gist.github.com/yescallop/5a97d010f226172fafab0933ce8ea8af
At first the KBD port was successfully set up by `i8042`, but then the
first initialization attempt by `atkbd` failed:
[ 2.698474] i8042: [17] f2 -> i8042 (kbd-data)
[ 2.698678] i8042: [17] fa <- i8042 (interrupt, 0, 1)
[ 2.698746] i8042: [17] 83 <- i8042 (interrupt, 0, 1)
[ 2.698767] i8042: [17] 60 -> i8042 (command)
[ 2.698856] i8042: [17] 66 -> i8042 (parameter)
[ 2.698951] i8042: [17] 60 -> i8042 (command)
[ 2.699092] i8042: [17] 67 -> i8042 (parameter)
It seems that the i8042 implementation on the laptop omitted the byte
0xab from its response to the Get ID command, thus making atkbd_probe()
fail on receiving an invalid keyboard ID (should normally be 0xab83).
This situation went on for a few rounds when I pressed and released the
space key (scan code: 0x39 when pressed, 0xb9 when released). The sixth
time I pressed the space key, something different happened:
[ 48.188540] i8042: [13664] 39 <- i8042 (interrupt, 0, 1)
[ 48.188658] i8042: [13664] f2 -> i8042 (kbd-data)
[ 48.188998] i8042: [13664] fa <- i8042 (interrupt, 0, 1)
[ 48.709743] i8042: [13821] ed -> i8042 (kbd-data)
[ 48.913069] i8042: [13882] 60 -> i8042 (command)
[ 48.913235] i8042: [13882] 66 -> i8042 (parameter)
[ 48.913446] i8042: [13882] 60 -> i8042 (command)
[ 48.913591] i8042: [13882] 67 -> i8042 (parameter)
[ 48.913672] i8042: [13882] fa <- i8042 (interrupt, 0, 0)
This time even the byte 0x83 was omitted, so the Get ID command failed
and atkbd_probe() tried to set the LEDs on the keyboard, but failed
again for not receiving an ACK to the command byte 0xed. However, when
i8042_port_close() was later called, an ACK was read from the KBD port,
which indicates that the i8042 implementation might have failed to raise
an interrupt for this ACK.
And the next time I released the space key, the byte 0x83 was omitted
again, but atkbd_probe() somehow succeeded in receiving an ACK to the
Set LEDs command, and the keyboard was finally initialized properly.
Until now, the only workaround is to boot with the kernel parameter
`i8042.dumbkbd`, which isn't very desirable as it disables the Caps Lock
LED. I have considered an alternative, generic fix that involves
flushing the keyboard buffer immediately after the Get ID command and
trying to set the LEDs even if the keyboard ID is invalid. This worked
on my laptop at least, but I really doubt that it won't cause any
regressions.
This patch adds a kernel parameter `atkbd.skip_id_check` that, when
enabled, skip the check of keyboard ID when probing and try to set the
LEDs directly, and adds quirks that enable the parameter by default on
`82NC` and `82TK` models, as these are the only models on which the
patch was tested to work.
Part of the patch is based on Bernhard's initial work.
Link: https://lore.kernel.org/linux-input/A3D566C5B262EC0F+4ede8371-9a20-6e5d-6a8c-b44d15634e26@mail2.sysu.edu.cn/
Cc: Bernhard Kaindl <bernhard.kaindl@cloud.com>
Reported-by: Bernhard Kaindl <bernhard.kaindl@cloud.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=216994
Signed-off-by: Shang Ye <yesh25@mail2.sysu.edu.cn>
---
V1 -> V2: Added kernel parameter and removed untested models
.../admin-guide/kernel-parameters.txt | 3 ++
drivers/input/keyboard/atkbd.c | 49 +++++++++++++++++--
2 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 6221a1d057dd..2a679a81e6fa 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -421,6 +421,9 @@
atkbd.scroll= [HW] Enable scroll wheel on MS Office and similar
keyboards
+ atkbd.skip_id_check=
+ [HW] Skip the check of keyboard ID when probing
+
atkbd.softraw= [HW] Choose between synthetic and real raw mode
Format: <bool> (0 = real, 1 = synthetic (default))
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index c92e544c792d..daf5a1821c42 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -65,6 +65,10 @@ static bool atkbd_terminal;
module_param_named(terminal, atkbd_terminal, bool, 0);
MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2");
+static bool atkbd_skip_id_check;
+module_param_named(skip_id_check, atkbd_skip_id_check, bool, 0);
+MODULE_PARM_DESC(skip_id_check, "Skip the check of keyboard ID when probing");
+
#define SCANCODE(keymap) ((keymap >> 16) & 0xFFFF)
#define KEYCODE(keymap) (keymap & 0xFFFF)
@@ -794,12 +798,12 @@ static int atkbd_probe(struct atkbd *atkbd)
*/
param[0] = param[1] = 0xa5; /* initialize with invalid values */
- if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) {
+ if (atkbd_skip_id_check || ps2_command(ps2dev, param, ATKBD_CMD_GETID)) {
/*
- * If the get ID command failed, we check if we can at least set the LEDs on
- * the keyboard. This should work on every keyboard out there. It also turns
- * the LEDs off, which we want anyway.
+ * If the Get ID command failed or is skipped, we check if we can at least set
+ * the LEDs on the keyboard. This should work on every keyboard out there. It
+ * also turns the LEDs off, which we want anyway.
*/
param[0] = 0;
if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
@@ -1751,6 +1755,12 @@ static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id)
return 1;
}
+static int __init atkbd_id_check_fixup(const struct dmi_system_id *id)
+{
+ atkbd_skip_id_check = true;
+ return 1;
+}
+
/*
* NOTE: do not add any more "force release" quirks to this table. The
* task of adjusting list of keys that should be "released" automatically
@@ -1900,6 +1910,37 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
},
.callback = atkbd_deactivate_fixup,
},
+ /*
+ * Some laptops have erroneous i8042 implementations that may randomly
+ * omit interrupts and/or bytes in response to the Get ID command and
+ * then cause atkbd_probe() to fail on receiving an invalid ID, or on
+ * receiving no ID and then no ACK to the consequent Set LEDs command.
+ * For this reason, we simply skip the Get ID command on these laptops
+ * to avoid any potential issues.
+ */
+ {
+ /*
+ * Lenovo Yoga 14sIHU 2021
+ * Lenovo XiaoXinPro 14IHU 2021
+ * Lenovo Yoga Slim 7 Pro 14IHU5
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82NC"),
+ },
+ .callback = atkbd_id_check_fixup,
+ },
+ {
+ /*
+ * Lenovo Yoga Pro 14s IAH7
+ * Lenovo Yoga Slim 7 ProX 14IAH7
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82TK"),
+ },
+ .callback = atkbd_id_check_fixup,
+ },
{ }
};
base-commit: b00315628095075da4af8d6d519d85d95117de09
--
2.40.1
^ permalink raw reply related
* Re: [PATCH 1/1] HID: logitech-hidpp: Add USB and Bluetooth IDs for the Logitech G915 TKL Keyboard
From: Bastien Nocera @ 2023-05-30 13:30 UTC (permalink / raw)
To: Stuart
Cc: jikos, Filipe Laíns, Benjamin Tissoires, linux-input,
linux-kernel
In-Reply-To: <CALTg27=2SeJmn9nka_JddSQv5NYFsz1qNLkR+q+j8-oMrnskiQ@mail.gmail.com>
On Tue, 2023-05-30 at 13:49 +0100, Stuart wrote:
> Using https://github.com/cvuchener/hidpp, the keyboard now seems to
> report the following:
While the list of all the feature it supports is interesting, it seems
that the only thing this adds on top of hid-generic is the battery
reporting. Can you please verify that that's the fact and add this
information to the commit message?
Cheers
> Logitech G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard
> (046d:c343) is a HID++ 4.2 device
> Feature 0x01: [0x0001] Feature set
> Feature 0x02: [0x0003] Device FW version
> Feature 0x03: [0x0005] Device name
> Feature 0x04: [0x1d4b] Wireless device status
> Feature 0x05: [0x0020] Reset
> Feature 0x06: [0x1001] Battery voltage
> Feature 0x07: [0x1814] Change host
> Feature 0x08: [0x1815] Hosts info
> Feature 0x09: [0x8071] RGB Effects
> Feature 0x0a: [0x8081] Per-key lighting v2
> Feature 0x0b: [0x1b04] Reprog controls v4
> Feature 0x0c: [0x1bc0] Persistent remappable action
> Feature 0x0d: [0x4100] Encryption
> Feature 0x0e: [0x4522] Disable keys by usage
> Feature 0x0f: [0x4540] Keyboard layout 2
> Feature 0x10: [0x8010] G-key
> Feature 0x11: [0x8020] M-keys
> Feature 0x12: [0x8030] MR
> Feature 0x13: [0x8040] Brightness control
> Feature 0x14: [0x8100] Onboard profiles
> Feature 0x15: [0x8060] Report rate
> Feature 0x16: [0x00c2] DFUcontrol 3
> Feature 0x17: [0x00d0] DFU
> Feature 0x18: [0x1802] Device reset (hidden, internal)
> Feature 0x19: [0x1803] ? (hidden, internal)
> Feature 0x1a: [0x1806] Configurable device properties (hidden,
> internal)
> Feature 0x1b: [0x1813] ? (hidden, internal)
> Feature 0x1c: [0x1805] OOBState (hidden, internal)
> Feature 0x1d: [0x1830] ? (hidden, internal)
> Feature 0x1e: [0x1890] ? (hidden, internal)
> Feature 0x1f: [0x1891] ? (hidden, internal)
> Feature 0x20: [0x18a1] ? (hidden, internal)
> Feature 0x21: [0x1e00] Enable hidden features (hidden)
> Feature 0x22: [0x1eb0] ? (hidden, internal)
> Feature 0x23: [0x1861] ? (hidden, internal)
> Feature 0x24: [0x18b0] ? (hidden, internal)
>
> On Tue, May 30, 2023 at 9:36 AM Bastien Nocera <hadess@hadess.net>
> wrote:
> > On Tue, 2023-05-30 at 00:30 +0100, Stuart Hayhurst wrote:
> > > From: stuarthayhurst <stuart.a.hayhurst@gmail.com>
> > >
> > > Adds the USB and Bluetooth IDs for the Logitech G915 TKL keyboard
> >
> > Which functionality gets enabled by that addition, battery
> > reporting,
> > something else?
> >
> > >
> > > ---
> > > drivers/hid/hid-logitech-hidpp.c | 4 ++++
> > > 1 file changed, 4 insertions(+)
> > >
> > > diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-
> > > logitech-hidpp.c
> > > index b2cd7527de19..28761272afe5 100644
> > > --- a/drivers/hid/hid-logitech-hidpp.c
> > > +++ b/drivers/hid/hid-logitech-hidpp.c
> > > @@ -4403,6 +4403,8 @@ static const struct hid_device_id
> > > hidpp_devices[] = {
> > > HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
> > > { /* Logitech G903 Hero Gaming Mouse over USB */
> > > HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) },
> > > + { /* Logitech G915 TKL Keyboard over USB */
> > > + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC343) },
> > > { /* Logitech G920 Wheel over USB */
> > > HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
> > > USB_DEVICE_ID_LOGITECH_G920_WHEEL),
> > > .driver_data = HIDPP_QUIRK_CLASS_G920 |
> > > HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
> > > @@ -4418,6 +4420,8 @@ static const struct hid_device_id
> > > hidpp_devices[] = {
> > > { /* MX5500 keyboard over Bluetooth */
> > > HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
> > > .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS
> > > },
> > > + { /* Logitech G915 TKL keyboard over Bluetooth */
> > > + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb35f) },
> > > { /* M-RCQ142 V470 Cordless Laser Mouse over Bluetooth */
> > > HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
> > > { /* MX Master mouse over Bluetooth */
> >
^ permalink raw reply
* Re: [PATCH] input: Add new keyboard backlight control keys to match modern notebooks
From: Bastien Nocera @ 2023-05-30 13:33 UTC (permalink / raw)
To: Werner Sembach, Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <20230530110550.18289-1-wse@tuxedocomputers.com>
On Tue, 2023-05-30 at 13:05 +0200, Werner Sembach wrote:
> The old three KEY_KBDILLUM* keycodes don't reflect the current
> situation
> modern notebooks anymore. Especially the ones with RGB keyboards.
>
> e.g.
> - Clevo NL50NU has a toggle, an up, a down and a color-cycle key
> - TongFang PH4ARX1 doesn't have a toggle key, but one that cycles
> through
> off, half-brightness, and full-brightness.
>
> Also, on some devices these keys are already implemented in firmware.
> It
> would still be nice if there is a way to let userspace know when one
> of
> these keys is pressed to display the OSD, but don't advice it to
> actually
> do anything. This is the intended purpose of the KEY_KBDILLUMCHANGE
> define.
>
> Signed-off-by: Werner Sembach <wse@tuxedocomputers.com>
Can you please point to the user-space patches (or issues filed) that
would integrate the support for those keycodes, and make the key
presses do something?
Has anyone tested that those keycodes are fit for purpose when mixed
with other brightness changes that don't happen through key presses?
> ---
> include/uapi/linux/input-event-codes.h | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/include/uapi/linux/input-event-codes.h
> b/include/uapi/linux/input-event-codes.h
> index 022a520e31fc2..05287bf9a77f7 100644
> --- a/include/uapi/linux/input-event-codes.h
> +++ b/include/uapi/linux/input-event-codes.h
> @@ -803,6 +803,10 @@
> #define BTN_TRIGGER_HAPPY39 0x2e6
> #define BTN_TRIGGER_HAPPY40 0x2e7
>
> +#define KEY_KBDILLUMCYCLE 0x2e8
> +#define KEY_KBDILLUMCOLORCYCLE 0x2e9
> +#define KEY_KBDILLUMCHANGE 0x2ea
> +
> /* We avoid low common keys in module aliases so they don't get
> huge. */
> #define KEY_MIN_INTERESTING KEY_MUTE
> #define KEY_MAX 0x2ff
^ permalink raw reply
* [PATCH] HID: HP: add support for 03f0:464a HP Elite Presenter Mouse
From: Marco Morandini @ 2023-05-30 13:40 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input
HP Elite Presenter Mouse HID Record Descriptor shows
two mouses (Repord ID 0x1 and 0x2), one keypad (Report ID 0x5),
two Consumer Controls (Report IDs 0x6 and 0x3).
Previous to this commit it registers one mouse, one keypad
and one Consumer Control, and it was usable only as a
digitl laser pointer (one of the two mouses). This patch defines
the 464a USB device ID and enables the HID_QUIRK_MULTI_INPUT
quirk for it, allowing to use the device both as a mouse
and a digital laser pointer.
Signed-off-by: Marco Morandini <marco.morandini@polimi.it>
---
drivers/hid/hid-ids.h | 1 +
drivers/hid/hid-quirks.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index d79e946acdcb..8afa1149cdf2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -619,6 +619,7 @@
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
#define USB_VENDOR_ID_HP 0x03f0
+#define USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A 0x464a
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 804fc03600cc..3983b4f282f8 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -96,6 +96,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096), HID_QUIRK_NO_INIT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A293), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A), HID_QUIRK_ALWAYS_POLL },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A), HID_QUIRK_ALWAYS_POLL },
--
2.40.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox