Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:

 - a revert of a patch resetting extra buttons on touchpads claiming to
   be buttonpads as this caused regression on certain Dell devices

 - a new driver for Mediatek MT6779 keypad

 - a new driver for Imagis touchscreen

 - rework of Google/Chrome OS "Vivaldi" keyboard handling

 - assorted driver fixes.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (31 commits)
  Revert "Input: clear BTN_RIGHT/MIDDLE on buttonpads"
  Input: adi - remove redundant variable z
  Input: add Imagis touchscreen driver
  dt-bindings: input/touchscreen: bindings for Imagis
  Input: synaptics - enable InterTouch on ThinkPad T14/P14s Gen 1 AMD
  Input: stmfts - fix reference leak in stmfts_input_open
  Input: add bounds checking to input_set_capability()
  Input: iqs5xx - use local input_dev pointer
  HID: google: modify HID device groups of eel
  HID: google: Add support for vivaldi to hid-hammer
  HID: google: extract Vivaldi hid feature mapping for use in hid-hammer
  Input: extract ChromeOS vivaldi physmap show function
  HID: google: switch to devm when registering keyboard backlight LED
  Input: mt6779-keypad - fix signedness bug
  Input: mt6779-keypad - add MediaTek keypad driver
  dt-bindings: input: Add bindings for Mediatek matrix keypad
  Input: da9063 - use devm_delayed_work_autocancel()
  Input: goodix - fix race on driver unbind
  Input: goodix - use input_copy_abs() helper
  Input: add input_copy_abs() function
  ...
This commit is contained in:
Linus Torvalds 2022-04-01 10:14:32 -07:00
commit aa240ee788
36 changed files with 1364 additions and 333 deletions

View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/mediatek,mt6779-keypad.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mediatek's Keypad Controller device tree bindings
maintainers:
- Fengping Yu <fengping.yu@mediatek.com>
allOf:
- $ref: "/schemas/input/matrix-keymap.yaml#"
description: |
Mediatek's Keypad controller is used to interface a SoC with a matrix-type
keypad device. The keypad controller supports multiple row and column lines.
A key can be placed at each intersection of a unique row and a unique column.
The keypad controller can sense a key-press and key-release and report the
event using a interrupt to the cpu.
properties:
compatible:
oneOf:
- const: mediatek,mt6779-keypad
- items:
- enum:
- mediatek,mt6873-keypad
- const: mediatek,mt6779-keypad
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: kpd
wakeup-source:
description: use any event on keypad as wakeup event
type: boolean
debounce-delay-ms:
maximum: 256
default: 16
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
keyboard@10010000 {
compatible = "mediatek,mt6779-keypad";
reg = <0 0x10010000 0 0x1000>;
interrupts = <GIC_SPI 75 IRQ_TYPE_EDGE_FALLING>;
clocks = <&clk26m>;
clock-names = "kpd";
};
};

View File

@ -9,7 +9,10 @@ For MT6397/MT6323 MFD bindings see:
Documentation/devicetree/bindings/mfd/mt6397.txt
Required properties:
- compatible: "mediatek,mt6397-keys" or "mediatek,mt6323-keys"
- compatible: Should be one of:
- "mediatek,mt6397-keys"
- "mediatek,mt6323-keys"
- "mediatek,mt6358-keys"
- linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml
Optional Properties:

View File

@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/imagis,ist3038c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Imagis IST30XXC family touchscreen controller bindings
maintainers:
- Markuss Broks <markuss.broks@gmail.com>
allOf:
- $ref: touchscreen.yaml#
properties:
$nodename:
pattern: "^touchscreen@[0-9a-f]+$"
compatible:
enum:
- imagis,ist3038c
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply:
description: Power supply regulator for the chip
vddio-supply:
description: Power supply regulator for the I2C bus
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-fuzz-x: true
touchscreen-fuzz-y: true
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
- touchscreen-size-x
- touchscreen-size-y
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@50 {
compatible = "imagis,ist3038c";
reg = <0x50>;
interrupt-parent = <&gpio>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&ldo1_reg>;
vddio-supply = <&ldo2_reg>;
touchscreen-size-x = <720>;
touchscreen-size-y = <1280>;
touchscreen-fuzz-x = <10>;
touchscreen-fuzz-y = <10>;
touchscreen-inverted-x;
touchscreen-inverted-y;
};
};
...

View File

@ -560,6 +560,8 @@ patternProperties:
description: Ingenieurburo Fur Ic-Technologie (I/F/I)
"^ilitek,.*":
description: ILI Technology Corporation (ILITEK)
"^imagis,.*":
description: Imagis Technologies Co., Ltd.
"^img,.*":
description: Imagination Technologies Ltd.
"^imi,.*":

View File

@ -9517,6 +9517,12 @@ M: Stanislaw Gruszka <stf_xl@wp.pl>
S: Maintained
F: drivers/usb/atm/ueagle-atm.c
IMAGIS TOUCHSCREEN DRIVER
M: Markuss Broks <markuss.broks@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/imagis,ist3038c.yaml
F: drivers/input/touchscreen/imagis.c
IMGTEC ASCII LCD DRIVER
M: Paul Burton <paulburton@kernel.org>
S: Maintained

View File

@ -405,14 +405,25 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.
config HID_VIVALDI_COMMON
tristate
help
ChromeOS Vivaldi HID parsing support library. This is a hidden
option so that drivers can use common code to parse the HID
descriptors for vivaldi function row keymap.
config HID_GOOGLE_HAMMER
tristate "Google Hammer Keyboard"
select HID_VIVALDI_COMMON
select INPUT_VIVALDIFMAP
depends on USB_HID && LEDS_CLASS && CROS_EC
help
Say Y here if you have a Google Hammer device.
config HID_VIVALDI
tristate "Vivaldi Keyboard"
select HID_VIVALDI_COMMON
select INPUT_VIVALDIFMAP
depends on HID
help
Say Y here if you want to enable support for Vivaldi keyboards.

View File

@ -50,6 +50,7 @@ obj-$(CONFIG_HID_FT260) += hid-ft260.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o
obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o
obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o
obj-$(CONFIG_HID_VIVALDI) += hid-vivaldi.o
obj-$(CONFIG_HID_GT683R) += hid-gt683r.o

View File

@ -15,6 +15,7 @@
#include <linux/acpi.h>
#include <linux/hid.h>
#include <linux/input/vivaldi-fmap.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/of.h>
@ -25,6 +26,7 @@
#include <asm/unaligned.h>
#include "hid-ids.h"
#include "hid-vivaldi-common.h"
/*
* C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting
@ -340,9 +342,9 @@ static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
static int hammer_register_leds(struct hid_device *hdev)
{
struct hammer_kbd_leds *kbd_backlight;
int error;
kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL);
kbd_backlight = devm_kzalloc(&hdev->dev, sizeof(*kbd_backlight),
GFP_KERNEL);
if (!kbd_backlight)
return -ENOMEM;
@ -356,26 +358,7 @@ static int hammer_register_leds(struct hid_device *hdev)
/* Set backlight to 0% initially. */
hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);
error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
if (error)
goto err_free_mem;
hid_set_drvdata(hdev, kbd_backlight);
return 0;
err_free_mem:
kfree(kbd_backlight);
return error;
}
static void hammer_unregister_leds(struct hid_device *hdev)
{
struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev);
if (kbd_backlight) {
led_classdev_unregister(&kbd_backlight->cdev);
kfree(kbd_backlight);
}
return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
}
#define HID_UP_GOOGLEVENDOR 0xffd10000
@ -512,11 +495,23 @@ static void hammer_get_folded_state(struct hid_device *hdev)
kfree(buf);
}
static void hammer_stop(void *hdev)
{
hid_hw_stop(hdev);
}
static int hammer_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
struct vivaldi_data *vdata;
int error;
vdata = devm_kzalloc(&hdev->dev, sizeof(*vdata), GFP_KERNEL);
if (!vdata)
return -ENOMEM;
hid_set_drvdata(hdev, vdata);
error = hid_parse(hdev);
if (error)
return error;
@ -525,6 +520,10 @@ static int hammer_probe(struct hid_device *hdev,
if (error)
return error;
error = devm_add_action(&hdev->dev, hammer_stop, hdev);
if (error)
return error;
/*
* We always want to poll for, and handle tablet mode events from
* devices that have folded usage, even when nobody has opened the input
@ -577,15 +576,13 @@ static void hammer_remove(struct hid_device *hdev)
spin_unlock_irqrestore(&cbas_ec_lock, flags);
}
hammer_unregister_leds(hdev);
hid_hw_stop(hdev);
/* Unregistering LEDs and stopping the hardware is done via devm */
}
static const struct hid_device_id hammer_devices[] = {
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_DON) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
{ HID_DEVICE(BUS_USB, HID_GROUP_VIVALDI,
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_EEL) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
@ -610,6 +607,8 @@ static struct hid_driver hammer_driver = {
.id_table = hammer_devices,
.probe = hammer_probe,
.remove = hammer_remove,
.feature_mapping = vivaldi_feature_mapping,
.input_configured = vivaldi_input_configured,
.input_mapping = hammer_input_mapping,
.event = hammer_event,
};

View File

@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Helpers for ChromeOS HID Vivaldi keyboards
*
* Copyright (C) 2022 Google, Inc
*/
#include <linux/export.h>
#include <linux/hid.h>
#include <linux/input/vivaldi-fmap.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include "hid-vivaldi-common.h"
#define MIN_FN_ROW_KEY 1
#define MAX_FN_ROW_KEY VIVALDI_MAX_FUNCTION_ROW_KEYS
#define HID_VD_FN_ROW_PHYSMAP 0x00000001
#define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
/**
* vivaldi_feature_mapping - Fill out vivaldi keymap data exposed via HID
* @hdev: HID device to parse
* @field: HID field to parse
* @usage: HID usage to parse
*
* Note: this function assumes that driver data attached to @hdev contains an
* instance of &struct vivaldi_data at the very beginning.
*/
void vivaldi_feature_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
struct vivaldi_data *data = hid_get_drvdata(hdev);
struct hid_report *report = field->report;
u8 *report_data, *buf;
u32 report_len;
unsigned int fn_key;
int ret;
if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
(usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
return;
fn_key = usage->hid & HID_USAGE;
if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
return;
if (fn_key > data->num_function_row_keys)
data->num_function_row_keys = fn_key;
report_data = buf = hid_alloc_report_buf(report, GFP_KERNEL);
if (!report_data)
return;
report_len = hid_report_len(report);
if (!report->id) {
/*
* hid_hw_raw_request() will stuff report ID (which will be 0)
* into the first byte of the buffer even for unnumbered
* reports, so we need to account for this to avoid getting
* -EOVERFLOW in return.
* Note that hid_alloc_report_buf() adds 7 bytes to the size
* so we can safely say that we have space for an extra byte.
*/
report_len++;
}
ret = hid_hw_raw_request(hdev, report->id, report_data,
report_len, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0) {
dev_warn(&hdev->dev, "failed to fetch feature %d\n",
field->report->id);
goto out;
}
if (!report->id) {
/*
* Undo the damage from hid_hw_raw_request() for unnumbered
* reports.
*/
report_data++;
report_len--;
}
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
report_len, 0);
if (ret) {
dev_warn(&hdev->dev, "failed to report feature %d\n",
field->report->id);
goto out;
}
data->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
field->value[usage->usage_index];
out:
kfree(buf);
}
EXPORT_SYMBOL_GPL(vivaldi_feature_mapping);
static ssize_t function_row_physmap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
struct vivaldi_data *data = hid_get_drvdata(hdev);
return vivaldi_function_row_physmap_show(data, buf);
}
static DEVICE_ATTR_RO(function_row_physmap);
static struct attribute *vivaldi_sysfs_attrs[] = {
&dev_attr_function_row_physmap.attr,
NULL
};
static const struct attribute_group vivaldi_attribute_group = {
.attrs = vivaldi_sysfs_attrs,
};
/**
* vivaldi_input_configured - Complete initialization of device using vivaldi map
* @hdev: HID device to which vivaldi attributes should be attached
* @hidinput: HID input device (unused)
*/
int vivaldi_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
struct vivaldi_data *data = hid_get_drvdata(hdev);
if (!data->num_function_row_keys)
return 0;
return devm_device_add_group(&hdev->dev, &vivaldi_attribute_group);
}
EXPORT_SYMBOL_GPL(vivaldi_input_configured);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _HID_VIVALDI_COMMON_H
#define _HID_VIVALDI_COMMON_H
struct hid_device;
struct hid_field;
struct hid_input;
struct hid_usage;
void vivaldi_feature_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage);
int vivaldi_input_configured(struct hid_device *hdev,
struct hid_input *hidinput);
#endif /* _HID_VIVALDI_COMMON_H */

View File

@ -8,48 +8,11 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/input/vivaldi-fmap.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#define MIN_FN_ROW_KEY 1
#define MAX_FN_ROW_KEY 24
#define HID_VD_FN_ROW_PHYSMAP 0x00000001
#define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
struct vivaldi_data {
u32 function_row_physmap[MAX_FN_ROW_KEY - MIN_FN_ROW_KEY + 1];
int max_function_row_key;
};
static ssize_t function_row_physmap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
ssize_t size = 0;
int i;
if (!drvdata->max_function_row_key)
return 0;
for (i = 0; i < drvdata->max_function_row_key; i++)
size += sprintf(buf + size, "%02X ",
drvdata->function_row_physmap[i]);
size += sprintf(buf + size, "\n");
return size;
}
static DEVICE_ATTR_RO(function_row_physmap);
static struct attribute *sysfs_attrs[] = {
&dev_attr_function_row_physmap.attr,
NULL
};
static const struct attribute_group input_attribute_group = {
.attrs = sysfs_attrs
};
#include "hid-vivaldi-common.h"
static int vivaldi_probe(struct hid_device *hdev,
const struct hid_device_id *id)
@ -70,86 +33,8 @@ static int vivaldi_probe(struct hid_device *hdev,
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
}
static void vivaldi_feature_mapping(struct hid_device *hdev,
struct hid_field *field,
struct hid_usage *usage)
{
struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
struct hid_report *report = field->report;
int fn_key;
int ret;
u32 report_len;
u8 *report_data, *buf;
if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
(usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
return;
fn_key = (usage->hid & HID_USAGE);
if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
return;
if (fn_key > drvdata->max_function_row_key)
drvdata->max_function_row_key = fn_key;
report_data = buf = hid_alloc_report_buf(report, GFP_KERNEL);
if (!report_data)
return;
report_len = hid_report_len(report);
if (!report->id) {
/*
* hid_hw_raw_request() will stuff report ID (which will be 0)
* into the first byte of the buffer even for unnumbered
* reports, so we need to account for this to avoid getting
* -EOVERFLOW in return.
* Note that hid_alloc_report_buf() adds 7 bytes to the size
* so we can safely say that we have space for an extra byte.
*/
report_len++;
}
ret = hid_hw_raw_request(hdev, report->id, report_data,
report_len, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0) {
dev_warn(&hdev->dev, "failed to fetch feature %d\n",
field->report->id);
goto out;
}
if (!report->id) {
/*
* Undo the damage from hid_hw_raw_request() for unnumbered
* reports.
*/
report_data++;
report_len--;
}
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
report_len, 0);
if (ret) {
dev_warn(&hdev->dev, "failed to report feature %d\n",
field->report->id);
goto out;
}
drvdata->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
field->value[usage->usage_index];
out:
kfree(buf);
}
static int vivaldi_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
return devm_device_add_group(&hdev->dev, &input_attribute_group);
}
static const struct hid_device_id vivaldi_table[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_VIVALDI, HID_ANY_ID,
HID_ANY_ID) },
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_VIVALDI, HID_ANY_ID, HID_ANY_ID) },
{ }
};

View File

@ -77,6 +77,13 @@ config INPUT_MATRIXKMAP
To compile this driver as a module, choose M here: the
module will be called matrix-keymap.
config INPUT_VIVALDIFMAP
tristate
help
ChromeOS Vivaldi keymap support library. This is a hidden
option so that drivers can use common code to parse and
expose the vivaldi function row keymap.
comment "Userland interfaces"
config INPUT_MOUSEDEV

View File

@ -12,6 +12,7 @@ input-core-y += touchscreen.o
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
obj-$(CONFIG_INPUT_VIVALDIFMAP) += vivaldi-fmap.o
obj-$(CONFIG_INPUT_LEDS) += input-leds.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o

View File

@ -47,6 +47,17 @@ static DEFINE_MUTEX(input_mutex);
static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
static const unsigned int input_max_code[EV_CNT] = {
[EV_KEY] = KEY_MAX,
[EV_REL] = REL_MAX,
[EV_ABS] = ABS_MAX,
[EV_MSC] = MSC_MAX,
[EV_SW] = SW_MAX,
[EV_LED] = LED_MAX,
[EV_SND] = SND_MAX,
[EV_FF] = FF_MAX,
};
static inline int is_event_supported(unsigned int code,
unsigned long *bm, unsigned int max)
{
@ -511,6 +522,9 @@ void input_set_abs_params(struct input_dev *dev, unsigned int axis,
{
struct input_absinfo *absinfo;
__set_bit(EV_ABS, dev->evbit);
__set_bit(axis, dev->absbit);
input_alloc_absinfo(dev);
if (!dev->absinfo)
return;
@ -520,12 +534,45 @@ void input_set_abs_params(struct input_dev *dev, unsigned int axis,
absinfo->maximum = max;
absinfo->fuzz = fuzz;
absinfo->flat = flat;
__set_bit(EV_ABS, dev->evbit);
__set_bit(axis, dev->absbit);
}
EXPORT_SYMBOL(input_set_abs_params);
/**
* input_copy_abs - Copy absinfo from one input_dev to another
* @dst: Destination input device to copy the abs settings to
* @dst_axis: ABS_* value selecting the destination axis
* @src: Source input device to copy the abs settings from
* @src_axis: ABS_* value selecting the source axis
*
* Set absinfo for the selected destination axis by copying it from
* the specified source input device's source axis.
* This is useful to e.g. setup a pen/stylus input-device for combined
* touchscreen/pen hardware where the pen uses the same coordinates as
* the touchscreen.
*/
void input_copy_abs(struct input_dev *dst, unsigned int dst_axis,
const struct input_dev *src, unsigned int src_axis)
{
/* src must have EV_ABS and src_axis set */
if (WARN_ON(!(test_bit(EV_ABS, src->evbit) &&
test_bit(src_axis, src->absbit))))
return;
/*
* input_alloc_absinfo() may have failed for the source. Our caller is
* expected to catch this when registering the input devices, which may
* happen after the input_copy_abs() call.
*/
if (!src->absinfo)
return;
input_set_capability(dst, EV_ABS, dst_axis);
if (!dst->absinfo)
return;
dst->absinfo[dst_axis] = src->absinfo[src_axis];
}
EXPORT_SYMBOL(input_copy_abs);
/**
* input_grab_device - grabs device for exclusive use
@ -2074,6 +2121,14 @@ EXPORT_SYMBOL(input_get_timestamp);
*/
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
if (type < EV_CNT && input_max_code[type] &&
code > input_max_code[type]) {
pr_err("%s: invalid code %u for type %u\n", __func__, code,
type);
dump_stack();
return;
}
switch (type) {
case EV_KEY:
__set_bit(code, dev->keybit);
@ -2085,9 +2140,6 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
case EV_ABS:
input_alloc_absinfo(dev);
if (!dev->absinfo)
return;
__set_bit(code, dev->absbit);
break;
@ -2285,12 +2337,6 @@ int input_register_device(struct input_dev *dev)
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Buttonpads should not map BTN_RIGHT and/or BTN_MIDDLE. */
if (test_bit(INPUT_PROP_BUTTONPAD, dev->propbit)) {
__clear_bit(BTN_RIGHT, dev->keybit);
__clear_bit(BTN_MIDDLE, dev->keybit);
}
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);

View File

@ -123,7 +123,7 @@ static void adi_read_packet(struct adi_port *port)
{
struct adi *adi = port->adi;
struct gameport *gameport = port->gameport;
unsigned char u, v, w, x, z;
unsigned char u, v, w, x;
int t[2], s[2], i;
unsigned long flags;
@ -136,7 +136,7 @@ static void adi_read_packet(struct adi_port *port)
local_irq_save(flags);
gameport_trigger(gameport);
v = z = gameport_read(gameport);
v = gameport_read(gameport);
do {
u = v;

View File

@ -131,7 +131,7 @@ static const struct xpad_device {
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
{ 0x045e, 0x0b12, "Microsoft Xbox One X pad", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
{ 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
{ 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 },

View File

@ -103,6 +103,7 @@ config KEYBOARD_ATKBD
select SERIO_LIBPS2
select SERIO_I8042 if ARCH_MIGHT_HAVE_PC_SERIO
select SERIO_GSCPS2 if GSC
select INPUT_VIVALDIFMAP
help
Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
you'll need this, unless you have a different type keyboard (USB, ADB
@ -749,6 +750,7 @@ config KEYBOARD_XTKBD
config KEYBOARD_CROS_EC
tristate "ChromeOS EC keyboard"
select INPUT_MATRIXKMAP
select INPUT_VIVALDIFMAP
depends on CROS_EC
help
Say Y here to enable the matrix keyboard used by ChromeOS devices
@ -779,6 +781,18 @@ config KEYBOARD_BCM
To compile this driver as a module, choose M here: the
module will be called bcm-keypad.
config KEYBOARD_MT6779
tristate "MediaTek Keypad Support"
depends on ARCH_MEDIATEK || COMPILE_TEST
select REGMAP_MMIO
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the keypad on MediaTek SoCs.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called mt6779-keypad.
config KEYBOARD_MTK_PMIC
tristate "MediaTek PMIC keys support"
depends on MFD_MT6397

View File

@ -44,6 +44,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
obj-$(CONFIG_KEYBOARD_MT6779) += mt6779-keypad.o
obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o

View File

@ -19,6 +19,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/vivaldi-fmap.h>
#include <linux/serio.h>
#include <linux/workqueue.h>
#include <linux/libps2.h>
@ -64,8 +65,6 @@ 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");
#define MAX_FUNCTION_ROW_KEYS 24
#define SCANCODE(keymap) ((keymap >> 16) & 0xFFFF)
#define KEYCODE(keymap) (keymap & 0xFFFF)
@ -237,8 +236,7 @@ struct atkbd {
/* Serializes reconnect(), attr->set() and event work */
struct mutex mutex;
u32 function_row_physmap[MAX_FUNCTION_ROW_KEYS];
int num_function_row_keys;
struct vivaldi_data vdata;
};
/*
@ -308,17 +306,7 @@ static struct attribute *atkbd_attributes[] = {
static ssize_t atkbd_show_function_row_physmap(struct atkbd *atkbd, char *buf)
{
ssize_t size = 0;
int i;
if (!atkbd->num_function_row_keys)
return 0;
for (i = 0; i < atkbd->num_function_row_keys; i++)
size += scnprintf(buf + size, PAGE_SIZE - size, "%02X ",
atkbd->function_row_physmap[i]);
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
return size;
return vivaldi_function_row_physmap_show(&atkbd->vdata, buf);
}
static umode_t atkbd_attr_is_visible(struct kobject *kobj,
@ -329,7 +317,7 @@ static umode_t atkbd_attr_is_visible(struct kobject *kobj,
struct atkbd *atkbd = serio_get_drvdata(serio);
if (attr == &atkbd_attr_function_row_physmap.attr &&
!atkbd->num_function_row_keys)
!atkbd->vdata.num_function_row_keys)
return 0;
return attr->mode;
@ -1206,10 +1194,11 @@ static void atkbd_parse_fwnode_data(struct serio *serio)
/* Parse "function-row-physmap" property */
n = device_property_count_u32(dev, "function-row-physmap");
if (n > 0 && n <= MAX_FUNCTION_ROW_KEYS &&
if (n > 0 && n <= VIVALDI_MAX_FUNCTION_ROW_KEYS &&
!device_property_read_u32_array(dev, "function-row-physmap",
atkbd->function_row_physmap, n)) {
atkbd->num_function_row_keys = n;
atkbd->vdata.function_row_physmap,
n)) {
atkbd->vdata.num_function_row_keys = n;
dev_dbg(dev, "FW reported %d function-row key locations\n", n);
}
}

View File

@ -15,6 +15,7 @@
#include <linux/bitops.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/vivaldi-fmap.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/notifier.h>
@ -27,8 +28,6 @@
#include <asm/unaligned.h>
#define MAX_NUM_TOP_ROW_KEYS 15
/**
* struct cros_ec_keyb - Structure representing EC keyboard device
*
@ -44,9 +43,7 @@
* @idev: The input device for the matrix keys.
* @bs_idev: The input device for non-matrix buttons and switches (or NULL).
* @notifier: interrupt event notifier for transport devices
* @function_row_physmap: An array of the encoded rows/columns for the top
* row function keys, in an order from left to right
* @num_function_row_keys: The number of top row keys in a custom keyboard
* @vdata: vivaldi function row data
*/
struct cros_ec_keyb {
unsigned int rows;
@ -64,8 +61,7 @@ struct cros_ec_keyb {
struct input_dev *bs_idev;
struct notifier_block notifier;
u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS];
size_t num_function_row_keys;
struct vivaldi_data vdata;
};
/**
@ -537,9 +533,9 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
int err;
struct property *prop;
const __be32 *p;
u16 *physmap;
u32 *physmap;
u32 key_pos;
int row, col;
unsigned int row, col, scancode, n_physmap;
err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols);
if (err)
@ -591,20 +587,21 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
ckdev->idev = idev;
cros_ec_keyb_compute_valid_keys(ckdev);
physmap = ckdev->function_row_physmap;
physmap = ckdev->vdata.function_row_physmap;
n_physmap = 0;
of_property_for_each_u32(dev->of_node, "function-row-physmap",
prop, p, key_pos) {
if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) {
if (n_physmap == VIVALDI_MAX_FUNCTION_ROW_KEYS) {
dev_warn(dev, "Only support up to %d top row keys\n",
MAX_NUM_TOP_ROW_KEYS);
VIVALDI_MAX_FUNCTION_ROW_KEYS);
break;
}
row = KEY_ROW(key_pos);
col = KEY_COL(key_pos);
*physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
physmap++;
ckdev->num_function_row_keys++;
scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
physmap[n_physmap++] = scancode;
}
ckdev->vdata.num_function_row_keys = n_physmap;
err = input_register_device(ckdev->idev);
if (err) {
@ -619,18 +616,10 @@ static ssize_t function_row_physmap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t size = 0;
int i;
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
u16 *physmap = ckdev->function_row_physmap;
const struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
const struct vivaldi_data *data = &ckdev->vdata;
for (i = 0; i < ckdev->num_function_row_keys; i++)
size += scnprintf(buf + size, PAGE_SIZE - size,
"%s%02X", size ? " " : "", physmap[i]);
if (size)
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
return size;
return vivaldi_function_row_physmap_show(data, buf);
}
static DEVICE_ATTR_RO(function_row_physmap);
@ -648,7 +637,7 @@ static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj,
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
if (attr == &dev_attr_function_row_physmap.attr &&
!ckdev->num_function_row_keys)
!ckdev->vdata.num_function_row_keys)
return 0;
return attr->mode;

View File

@ -0,0 +1,221 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 MediaTek Inc.
* Author Fengping Yu <fengping.yu@mediatek.com>
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/input/matrix_keypad.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define MTK_KPD_NAME "mt6779-keypad"
#define MTK_KPD_MEM 0x0004
#define MTK_KPD_DEBOUNCE 0x0018
#define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0)
#define MTK_KPD_DEBOUNCE_MAX_MS 256
#define MTK_KPD_NUM_MEMS 5
#define MTK_KPD_NUM_BITS 136 /* 4*32+8 MEM5 only use 8 BITS */
struct mt6779_keypad {
struct regmap *regmap;
struct input_dev *input_dev;
struct clk *clk;
void __iomem *base;
u32 n_rows;
u32 n_cols;
DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS);
};
static const struct regmap_config mt6779_keypad_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = sizeof(u32),
.max_register = 36,
};
static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id)
{
struct mt6779_keypad *keypad = dev_id;
const unsigned short *keycode = keypad->input_dev->keycode;
DECLARE_BITMAP(new_state, MTK_KPD_NUM_BITS);
DECLARE_BITMAP(change, MTK_KPD_NUM_BITS);
unsigned int bit_nr;
unsigned int row, col;
unsigned int scancode;
unsigned int row_shift = get_count_order(keypad->n_cols);
bool pressed;
regmap_bulk_read(keypad->regmap, MTK_KPD_MEM,
new_state, MTK_KPD_NUM_MEMS);
bitmap_xor(change, new_state, keypad->keymap_state, MTK_KPD_NUM_BITS);
for_each_set_bit(bit_nr, change, MTK_KPD_NUM_BITS) {
/*
* Registers are 32bits, but only bits [15:0] are used to
* indicate key status.
*/
if (bit_nr % 32 >= 16)
continue;
row = bit_nr / 32;
col = bit_nr % 32;
scancode = MATRIX_SCAN_CODE(row, col, row_shift);
/* 1: not pressed, 0: pressed */
pressed = !test_bit(bit_nr, new_state);
dev_dbg(&keypad->input_dev->dev, "%s",
pressed ? "pressed" : "released");
input_event(keypad->input_dev, EV_MSC, MSC_SCAN, scancode);
input_report_key(keypad->input_dev, keycode[scancode], pressed);
input_sync(keypad->input_dev);
dev_dbg(&keypad->input_dev->dev,
"report Linux keycode = %d\n", keycode[scancode]);
}
bitmap_copy(keypad->keymap_state, new_state, MTK_KPD_NUM_BITS);
return IRQ_HANDLED;
}
static void mt6779_keypad_clk_disable(void *data)
{
clk_disable_unprepare(data);
}
static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
{
struct mt6779_keypad *keypad;
int irq;
u32 debounce;
bool wakeup;
int error;
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
if (!keypad)
return -ENOMEM;
keypad->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(keypad->base))
return PTR_ERR(keypad->base);
keypad->regmap = devm_regmap_init_mmio(&pdev->dev, keypad->base,
&mt6779_keypad_regmap_cfg);
if (IS_ERR(keypad->regmap)) {
dev_err(&pdev->dev,
"regmap init failed:%pe\n", keypad->regmap);
return PTR_ERR(keypad->regmap);
}
bitmap_fill(keypad->keymap_state, MTK_KPD_NUM_BITS);
keypad->input_dev = devm_input_allocate_device(&pdev->dev);
if (!keypad->input_dev) {
dev_err(&pdev->dev, "Failed to allocate input dev\n");
return -ENOMEM;
}
keypad->input_dev->name = MTK_KPD_NAME;
keypad->input_dev->id.bustype = BUS_HOST;
error = matrix_keypad_parse_properties(&pdev->dev, &keypad->n_rows,
&keypad->n_cols);
if (error) {
dev_err(&pdev->dev, "Failed to parse keypad params\n");
return error;
}
if (device_property_read_u32(&pdev->dev, "debounce-delay-ms",
&debounce))
debounce = 16;
if (debounce > MTK_KPD_DEBOUNCE_MAX_MS) {
dev_err(&pdev->dev,
"Debounce time exceeds the maximum allowed time %dms\n",
MTK_KPD_DEBOUNCE_MAX_MS);
return -EINVAL;
}
wakeup = device_property_read_bool(&pdev->dev, "wakeup-source");
dev_dbg(&pdev->dev, "n_row=%d n_col=%d debounce=%d\n",
keypad->n_rows, keypad->n_cols, debounce);
error = matrix_keypad_build_keymap(NULL, NULL,
keypad->n_rows, keypad->n_cols,
NULL, keypad->input_dev);
if (error) {
dev_err(&pdev->dev, "Failed to build keymap\n");
return error;
}
input_set_capability(keypad->input_dev, EV_MSC, MSC_SCAN);
regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE,
(debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK);
keypad->clk = devm_clk_get(&pdev->dev, "kpd");
if (IS_ERR(keypad->clk))
return PTR_ERR(keypad->clk);
error = clk_prepare_enable(keypad->clk);
if (error) {
dev_err(&pdev->dev, "cannot prepare/enable keypad clock\n");
return error;
}
error = devm_add_action_or_reset(&pdev->dev, mt6779_keypad_clk_disable,
keypad->clk);
if (error)
return error;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
error = devm_request_threaded_irq(&pdev->dev, irq,
NULL, mt6779_keypad_irq_handler,
IRQF_ONESHOT, MTK_KPD_NAME, keypad);
if (error) {
dev_err(&pdev->dev, "Failed to request IRQ#%d: %d\n",
irq, error);
return error;
}
error = input_register_device(keypad->input_dev);
if (error) {
dev_err(&pdev->dev, "Failed to register device\n");
return error;
}
error = device_init_wakeup(&pdev->dev, wakeup);
if (error)
dev_warn(&pdev->dev, "device_init_wakeup() failed: %d\n",
error);
return 0;
}
static const struct of_device_id mt6779_keypad_of_match[] = {
{ .compatible = "mediatek,mt6779-keypad" },
{ .compatible = "mediatek,mt6873-keypad" },
{ /* sentinel */ }
};
static struct platform_driver mt6779_keypad_pdrv = {
.probe = mt6779_keypad_pdrv_probe,
.driver = {
.name = MTK_KPD_NAME,
.of_match_table = mt6779_keypad_of_match,
},
};
module_platform_driver(mt6779_keypad_pdrv);
MODULE_AUTHOR("Mediatek Corporation");
MODULE_DESCRIPTION("MTK Keypad (KPD) Driver");
MODULE_LICENSE("GPL");

View File

@ -9,6 +9,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/mt6323/registers.h>
#include <linux/mfd/mt6358/registers.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6397/registers.h>
#include <linux/module.h>
@ -74,11 +75,22 @@ static const struct mtk_pmic_regs mt6323_regs = {
.pmic_rst_reg = MT6323_TOP_RST_MISC,
};
static const struct mtk_pmic_regs mt6358_regs = {
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS,
0x2, MT6358_PSC_TOP_INT_CON0, 0x5),
.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS,
0x8, MT6358_PSC_TOP_INT_CON0, 0xa),
.pmic_rst_reg = MT6358_TOP_RST_MISC,
};
struct mtk_pmic_keys_info {
struct mtk_pmic_keys *keys;
const struct mtk_pmic_keys_regs *regs;
unsigned int keycode;
int irq;
int irq_r; /* optional: release irq if different */
bool wakeup:1;
};
@ -188,6 +200,18 @@ static int mtk_pmic_key_setup(struct mtk_pmic_keys *keys,
return ret;
}
if (info->irq_r > 0) {
ret = devm_request_threaded_irq(keys->dev, info->irq_r, NULL,
mtk_pmic_keys_irq_handler_thread,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"mtk-pmic-keys", info);
if (ret) {
dev_err(keys->dev, "Failed to request IRQ_r: %d: %d\n",
info->irq, ret);
return ret;
}
}
input_set_capability(keys->input_dev, EV_KEY, info->keycode);
return 0;
@ -199,8 +223,11 @@ static int __maybe_unused mtk_pmic_keys_suspend(struct device *dev)
int index;
for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
if (keys->keys[index].wakeup)
if (keys->keys[index].wakeup) {
enable_irq_wake(keys->keys[index].irq);
if (keys->keys[index].irq_r > 0)
enable_irq_wake(keys->keys[index].irq_r);
}
}
return 0;
@ -212,8 +239,11 @@ static int __maybe_unused mtk_pmic_keys_resume(struct device *dev)
int index;
for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
if (keys->keys[index].wakeup)
if (keys->keys[index].wakeup) {
disable_irq_wake(keys->keys[index].irq);
if (keys->keys[index].irq_r > 0)
disable_irq_wake(keys->keys[index].irq_r);
}
}
return 0;
@ -229,6 +259,9 @@ static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = {
}, {
.compatible = "mediatek,mt6323-keys",
.data = &mt6323_regs,
}, {
.compatible = "mediatek,mt6358-keys",
.data = &mt6358_regs,
}, {
/* sentinel */
}
@ -241,6 +274,8 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev)
unsigned int keycount;
struct mt6397_chip *pmic_chip = dev_get_drvdata(pdev->dev.parent);
struct device_node *node = pdev->dev.of_node, *child;
static const char *const irqnames[] = { "powerkey", "homekey" };
static const char *const irqnames_r[] = { "powerkey_r", "homekey_r" };
struct mtk_pmic_keys *keys;
const struct mtk_pmic_regs *mtk_pmic_regs;
struct input_dev *input_dev;
@ -268,7 +303,8 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev)
input_dev->id.version = 0x0001;
keycount = of_get_available_child_count(node);
if (keycount > MTK_PMIC_MAX_KEY_COUNT) {
if (keycount > MTK_PMIC_MAX_KEY_COUNT ||
keycount > ARRAY_SIZE(irqnames)) {
dev_err(keys->dev, "too many keys defined (%d)\n", keycount);
return -EINVAL;
}
@ -276,12 +312,23 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev)
for_each_child_of_node(node, child) {
keys->keys[index].regs = &mtk_pmic_regs->keys_regs[index];
keys->keys[index].irq = platform_get_irq(pdev, index);
keys->keys[index].irq =
platform_get_irq_byname(pdev, irqnames[index]);
if (keys->keys[index].irq < 0) {
of_node_put(child);
return keys->keys[index].irq;
}
if (of_device_is_compatible(node, "mediatek,mt6358-keys")) {
keys->keys[index].irq_r = platform_get_irq_byname(pdev,
irqnames_r[index]);
if (keys->keys[index].irq_r < 0) {
of_node_put(child);
return keys->keys[index].irq_r;
}
}
error = of_property_read_u32(child,
"linux,keycodes", &keys->keys[index].keycode);
if (error) {

View File

@ -4,6 +4,7 @@
* Copyright (C) 2015 Dialog Semiconductor Ltd.
*/
#include <linux/devm-helpers.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/input.h>
@ -182,13 +183,6 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
static void da9063_cancel_poll(void *data)
{
struct da9063_onkey *onkey = data;
cancel_delayed_work_sync(&onkey->work);
}
static int da9063_onkey_probe(struct platform_device *pdev)
{
struct da9063_onkey *onkey;
@ -234,9 +228,8 @@ static int da9063_onkey_probe(struct platform_device *pdev)
input_set_capability(onkey->input, EV_KEY, KEY_POWER);
INIT_DELAYED_WORK(&onkey->work, da9063_poll_on);
error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey);
error = devm_delayed_work_autocancel(&pdev->dev, &onkey->work,
da9063_poll_on);
if (error) {
dev_err(&pdev->dev,
"Failed to add cancel poll action: %d\n",

View File

@ -186,6 +186,7 @@ static const char * const smbus_pnp_ids[] = {
"LEN2044", /* L470 */
"LEN2054", /* E480 */
"LEN2055", /* E580 */
"LEN2064", /* T14 Gen 1 AMD / P14s Gen 1 AMD */
"LEN2068", /* T14 Gen 1 */
"SYN3052", /* HP EliteBook 840 G4 */
"SYN3221", /* HP 15-ay000 */

View File

@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/timekeeping.h>
#define DRIVER_NAME "ps2-gpio"
@ -36,14 +37,37 @@
#define PS2_DATA_BIT7 8
#define PS2_PARITY_BIT 9
#define PS2_STOP_BIT 10
#define PS2_TX_TIMEOUT 11
#define PS2_ACK_BIT 12
#define PS2_ACK_BIT 11
#define PS2_DEV_RET_ACK 0xfa
#define PS2_DEV_RET_NACK 0xfe
#define PS2_CMD_RESEND 0xfe
/*
* The PS2 protocol specifies a clock frequency between 10kHz and 16.7kHz,
* therefore the maximal interrupt interval should be 100us and the minimum
* interrupt interval should be ~60us. Let's allow +/- 20us for frequency
* deviations and interrupt latency.
*
* The data line must be samples after ~30us to 50us after the falling edge,
* since the device updates the data line at the rising edge.
*
* ___ ______ ______ ______ ___
* \ / \ / \ / \ /
* \ / \ / \ / \ /
* \______/ \______/ \______/ \______/
*
* |-----------------| |--------|
* 60us/100us 30us/50us
*/
#define PS2_CLK_FREQ_MIN_HZ 10000
#define PS2_CLK_FREQ_MAX_HZ 16700
#define PS2_CLK_MIN_INTERVAL_US ((1000 * 1000) / PS2_CLK_FREQ_MAX_HZ)
#define PS2_CLK_MAX_INTERVAL_US ((1000 * 1000) / PS2_CLK_FREQ_MIN_HZ)
#define PS2_IRQ_MIN_INTERVAL_US (PS2_CLK_MIN_INTERVAL_US - 20)
#define PS2_IRQ_MAX_INTERVAL_US (PS2_CLK_MAX_INTERVAL_US + 20)
struct ps2_gpio_data {
struct device *dev;
struct serio *serio;
@ -52,19 +76,30 @@ struct ps2_gpio_data {
struct gpio_desc *gpio_data;
bool write_enable;
int irq;
unsigned char rx_cnt;
unsigned char rx_byte;
unsigned char tx_cnt;
unsigned char tx_byte;
struct completion tx_done;
struct mutex tx_mutex;
struct delayed_work tx_work;
ktime_t t_irq_now;
ktime_t t_irq_last;
struct {
unsigned char cnt;
unsigned char byte;
} rx;
struct {
unsigned char cnt;
unsigned char byte;
ktime_t t_xfer_start;
ktime_t t_xfer_end;
struct completion complete;
struct mutex mutex;
struct delayed_work work;
} tx;
};
static int ps2_gpio_open(struct serio *serio)
{
struct ps2_gpio_data *drvdata = serio->port_data;
drvdata->t_irq_last = 0;
drvdata->tx.t_xfer_end = 0;
enable_irq(drvdata->irq);
return 0;
}
@ -73,7 +108,7 @@ static void ps2_gpio_close(struct serio *serio)
{
struct ps2_gpio_data *drvdata = serio->port_data;
flush_delayed_work(&drvdata->tx_work);
flush_delayed_work(&drvdata->tx.work);
disable_irq(drvdata->irq);
}
@ -85,9 +120,9 @@ static int __ps2_gpio_write(struct serio *serio, unsigned char val)
gpiod_direction_output(drvdata->gpio_clk, 0);
drvdata->mode = PS2_MODE_TX;
drvdata->tx_byte = val;
drvdata->tx.byte = val;
schedule_delayed_work(&drvdata->tx_work, usecs_to_jiffies(200));
schedule_delayed_work(&drvdata->tx.work, usecs_to_jiffies(200));
return 0;
}
@ -98,12 +133,12 @@ static int ps2_gpio_write(struct serio *serio, unsigned char val)
int ret = 0;
if (in_task()) {
mutex_lock(&drvdata->tx_mutex);
mutex_lock(&drvdata->tx.mutex);
__ps2_gpio_write(serio, val);
if (!wait_for_completion_timeout(&drvdata->tx_done,
if (!wait_for_completion_timeout(&drvdata->tx.complete,
msecs_to_jiffies(10000)))
ret = SERIO_TIMEOUT;
mutex_unlock(&drvdata->tx_mutex);
mutex_unlock(&drvdata->tx.mutex);
} else {
__ps2_gpio_write(serio, val);
}
@ -115,9 +150,10 @@ static void ps2_gpio_tx_work_fn(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct ps2_gpio_data *drvdata = container_of(dwork,
struct ps2_gpio_data,
tx_work);
struct ps2_gpio_data,
tx.work);
drvdata->tx.t_xfer_start = ktime_get();
enable_irq(drvdata->irq);
gpiod_direction_output(drvdata->gpio_data, 0);
gpiod_direction_input(drvdata->gpio_clk);
@ -128,20 +164,31 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
unsigned char byte, cnt;
int data;
int rxflags = 0;
static unsigned long old_jiffies;
s64 us_delta;
byte = drvdata->rx_byte;
cnt = drvdata->rx_cnt;
byte = drvdata->rx.byte;
cnt = drvdata->rx.cnt;
if (old_jiffies == 0)
old_jiffies = jiffies;
drvdata->t_irq_now = ktime_get();
if ((jiffies - old_jiffies) > usecs_to_jiffies(100)) {
/*
* We need to consider spurious interrupts happening right after
* a TX xfer finished.
*/
us_delta = ktime_us_delta(drvdata->t_irq_now, drvdata->tx.t_xfer_end);
if (unlikely(us_delta < PS2_IRQ_MIN_INTERVAL_US))
goto end;
us_delta = ktime_us_delta(drvdata->t_irq_now, drvdata->t_irq_last);
if (us_delta > PS2_IRQ_MAX_INTERVAL_US && cnt) {
dev_err(drvdata->dev,
"RX: timeout, probably we missed an interrupt\n");
goto err;
} else if (unlikely(us_delta < PS2_IRQ_MIN_INTERVAL_US)) {
/* Ignore spurious IRQs. */
goto end;
}
old_jiffies = jiffies;
drvdata->t_irq_last = drvdata->t_irq_now;
data = gpiod_get_value(drvdata->gpio_data);
if (unlikely(data < 0)) {
@ -178,8 +225,16 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
if (!drvdata->write_enable)
goto err;
}
break;
case PS2_STOP_BIT:
/* stop bit should be high */
if (unlikely(!data)) {
dev_err(drvdata->dev, "RX: stop bit should be high\n");
goto err;
}
/* Do not send spurious ACK's and NACK's when write fn is
/*
* Do not send spurious ACK's and NACK's when write fn is
* not provided.
*/
if (!drvdata->write_enable) {
@ -189,23 +244,11 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
break;
}
/* Let's send the data without waiting for the stop bit to be
* sent. It may happen that we miss the stop bit. When this
* happens we have no way to recover from this, certainly
* missing the parity bit would be recognized when processing
* the stop bit. When missing both, data is lost.
*/
serio_interrupt(drvdata->serio, byte, rxflags);
dev_dbg(drvdata->dev, "RX: sending byte 0x%x\n", byte);
break;
case PS2_STOP_BIT:
/* stop bit should be high */
if (unlikely(!data)) {
dev_err(drvdata->dev, "RX: stop bit should be high\n");
goto err;
}
cnt = byte = 0;
old_jiffies = 0;
goto end; /* success */
default:
dev_err(drvdata->dev, "RX: got out of sync with the device\n");
@ -217,11 +260,10 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
err:
cnt = byte = 0;
old_jiffies = 0;
__ps2_gpio_write(drvdata->serio, PS2_CMD_RESEND);
end:
drvdata->rx_cnt = cnt;
drvdata->rx_byte = byte;
drvdata->rx.cnt = cnt;
drvdata->rx.byte = byte;
return IRQ_HANDLED;
}
@ -229,20 +271,34 @@ static irqreturn_t ps2_gpio_irq_tx(struct ps2_gpio_data *drvdata)
{
unsigned char byte, cnt;
int data;
static unsigned long old_jiffies;
s64 us_delta;
cnt = drvdata->tx_cnt;
byte = drvdata->tx_byte;
cnt = drvdata->tx.cnt;
byte = drvdata->tx.byte;
if (old_jiffies == 0)
old_jiffies = jiffies;
drvdata->t_irq_now = ktime_get();
if ((jiffies - old_jiffies) > usecs_to_jiffies(100)) {
/*
* There might be pending IRQs since we disabled IRQs in
* __ps2_gpio_write(). We can expect at least one clock period until
* the device generates the first falling edge after releasing the
* clock line.
*/
us_delta = ktime_us_delta(drvdata->t_irq_now,
drvdata->tx.t_xfer_start);
if (unlikely(us_delta < PS2_CLK_MIN_INTERVAL_US))
goto end;
us_delta = ktime_us_delta(drvdata->t_irq_now, drvdata->t_irq_last);
if (us_delta > PS2_IRQ_MAX_INTERVAL_US && cnt > 1) {
dev_err(drvdata->dev,
"TX: timeout, probably we missed an interrupt\n");
goto err;
} else if (unlikely(us_delta < PS2_IRQ_MIN_INTERVAL_US)) {
/* Ignore spurious IRQs. */
goto end;
}
old_jiffies = jiffies;
drvdata->t_irq_last = drvdata->t_irq_now;
switch (cnt) {
case PS2_START_BIT:
@ -270,27 +326,22 @@ static irqreturn_t ps2_gpio_irq_tx(struct ps2_gpio_data *drvdata)
/* release data line to generate stop bit */
gpiod_direction_input(drvdata->gpio_data);
break;
case PS2_TX_TIMEOUT:
/* Devices generate one extra clock pulse before sending the
* acknowledgment.
*/
break;
case PS2_ACK_BIT:
gpiod_direction_input(drvdata->gpio_data);
data = gpiod_get_value(drvdata->gpio_data);
if (data) {
dev_warn(drvdata->dev, "TX: received NACK, retry\n");
goto err;
}
drvdata->tx.t_xfer_end = ktime_get();
drvdata->mode = PS2_MODE_RX;
complete(&drvdata->tx_done);
complete(&drvdata->tx.complete);
cnt = 1;
old_jiffies = 0;
goto end; /* success */
default:
/* Probably we missed the stop bit. Therefore we release data
/*
* Probably we missed the stop bit. Therefore we release data
* line and try again.
*/
gpiod_direction_input(drvdata->gpio_data);
@ -303,11 +354,10 @@ static irqreturn_t ps2_gpio_irq_tx(struct ps2_gpio_data *drvdata)
err:
cnt = 1;
old_jiffies = 0;
gpiod_direction_input(drvdata->gpio_data);
__ps2_gpio_write(drvdata->serio, drvdata->tx_byte);
__ps2_gpio_write(drvdata->serio, drvdata->tx.byte);
end:
drvdata->tx_cnt = cnt;
drvdata->tx.cnt = cnt;
return IRQ_HANDLED;
}
@ -322,14 +372,19 @@ static irqreturn_t ps2_gpio_irq(int irq, void *dev_id)
static int ps2_gpio_get_props(struct device *dev,
struct ps2_gpio_data *drvdata)
{
drvdata->gpio_data = devm_gpiod_get(dev, "data", GPIOD_IN);
enum gpiod_flags gflags;
/* Enforce open drain, since this is required by the PS/2 bus. */
gflags = GPIOD_IN | GPIOD_FLAGS_BIT_OPEN_DRAIN;
drvdata->gpio_data = devm_gpiod_get(dev, "data", gflags);
if (IS_ERR(drvdata->gpio_data)) {
dev_err(dev, "failed to request data gpio: %ld",
PTR_ERR(drvdata->gpio_data));
return PTR_ERR(drvdata->gpio_data);
}
drvdata->gpio_clk = devm_gpiod_get(dev, "clk", GPIOD_IN);
drvdata->gpio_clk = devm_gpiod_get(dev, "clk", gflags);
if (IS_ERR(drvdata->gpio_clk)) {
dev_err(dev, "failed to request clock gpio: %ld",
PTR_ERR(drvdata->gpio_clk));
@ -387,7 +442,8 @@ static int ps2_gpio_probe(struct platform_device *pdev)
serio->id.type = SERIO_8042;
serio->open = ps2_gpio_open;
serio->close = ps2_gpio_close;
/* Write can be enabled in platform/dt data, but possibly it will not
/*
* Write can be enabled in platform/dt data, but possibly it will not
* work because of the tough timings.
*/
serio->write = drvdata->write_enable ? ps2_gpio_write : NULL;
@ -400,14 +456,15 @@ static int ps2_gpio_probe(struct platform_device *pdev)
drvdata->dev = dev;
drvdata->mode = PS2_MODE_RX;
/* Tx count always starts at 1, as the start bit is sent implicitly by
/*
* Tx count always starts at 1, as the start bit is sent implicitly by
* host-to-device communication initialization.
*/
drvdata->tx_cnt = 1;
drvdata->tx.cnt = 1;
INIT_DELAYED_WORK(&drvdata->tx_work, ps2_gpio_tx_work_fn);
init_completion(&drvdata->tx_done);
mutex_init(&drvdata->tx_mutex);
INIT_DELAYED_WORK(&drvdata->tx.work, ps2_gpio_tx_work_fn);
init_completion(&drvdata->tx.complete);
mutex_init(&drvdata->tx.mutex);
serio_register_port(serio);
platform_set_drvdata(pdev, drvdata);

View File

@ -638,6 +638,16 @@ config TOUCHSCREEN_MTOUCH
To compile this driver as a module, choose M here: the
module will be called mtouch.
config TOUCHSCREEN_IMAGIS
tristate "Imagis touchscreen support"
depends on I2C
help
Say Y here if you have an Imagis IST30xxC touchscreen.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called imagis.
config TOUCHSCREEN_IMX6UL_TSC
tristate "Freescale i.MX6UL touchscreen controller"
depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM

View File

@ -49,6 +49,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_ILITEK) += ilitek_ts_i2c.o
obj-$(CONFIG_TOUCHSCREEN_IMAGIS) += imagis.o
obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o

View File

@ -298,32 +298,17 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
return -ENOMSG;
}
static struct input_dev *goodix_create_pen_input(struct goodix_ts_data *ts)
static int goodix_create_pen_input(struct goodix_ts_data *ts)
{
struct device *dev = &ts->client->dev;
struct input_dev *input;
input = devm_input_allocate_device(dev);
if (!input)
return NULL;
return -ENOMEM;
input_alloc_absinfo(input);
if (!input->absinfo) {
input_free_device(input);
return NULL;
}
input->absinfo[ABS_X] = ts->input_dev->absinfo[ABS_MT_POSITION_X];
input->absinfo[ABS_Y] = ts->input_dev->absinfo[ABS_MT_POSITION_Y];
__set_bit(ABS_X, input->absbit);
__set_bit(ABS_Y, input->absbit);
input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0);
input_set_capability(input, EV_KEY, BTN_TOUCH);
input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
input_set_capability(input, EV_KEY, BTN_STYLUS);
input_set_capability(input, EV_KEY, BTN_STYLUS2);
__set_bit(INPUT_PROP_DIRECT, input->propbit);
input_copy_abs(input, ABS_X, ts->input_dev, ABS_MT_POSITION_X);
input_copy_abs(input, ABS_Y, ts->input_dev, ABS_MT_POSITION_Y);
/*
* The resolution of these touchscreens is about 10 units/mm, the actual
* resolution does not matter much since we set INPUT_PROP_DIRECT.
@ -331,6 +316,13 @@ static struct input_dev *goodix_create_pen_input(struct goodix_ts_data *ts)
*/
input_abs_set_res(input, ABS_X, 10);
input_abs_set_res(input, ABS_Y, 10);
input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0);
input_set_capability(input, EV_KEY, BTN_TOUCH);
input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
input_set_capability(input, EV_KEY, BTN_STYLUS);
input_set_capability(input, EV_KEY, BTN_STYLUS2);
__set_bit(INPUT_PROP_DIRECT, input->propbit);
input->name = "Goodix Active Pen";
input->phys = "input/pen";
@ -340,25 +332,23 @@ static struct input_dev *goodix_create_pen_input(struct goodix_ts_data *ts)
input->id.product = 0x1001;
input->id.version = ts->version;
if (input_register_device(input) != 0) {
input_free_device(input);
return NULL;
}
return input;
ts->input_pen = input;
return 0;
}
static void goodix_ts_report_pen_down(struct goodix_ts_data *ts, u8 *data)
{
int input_x, input_y, input_w;
int input_x, input_y, input_w, error;
u8 key_value;
if (!ts->input_pen) {
ts->input_pen = goodix_create_pen_input(ts);
if (!ts->input_pen)
return;
if (!ts->pen_input_registered) {
error = input_register_device(ts->input_pen);
ts->pen_input_registered = (error == 0) ? 1 : error;
}
if (ts->pen_input_registered < 0)
return;
if (ts->contact_size == 9) {
input_x = get_unaligned_le16(&data[4]);
input_y = get_unaligned_le16(&data[6]);
@ -1215,6 +1205,17 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
return error;
}
/*
* Create the input_pen device before goodix_request_irq() calls
* devm_request_threaded_irq() so that the devm framework frees
* it after disabling the irq.
* Unfortunately there is no way to detect if the touchscreen has pen
* support, so registering the dev is delayed till the first pen event.
*/
error = goodix_create_pen_input(ts);
if (error)
return error;
ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
error = goodix_request_irq(ts);
if (error) {

View File

@ -94,6 +94,7 @@ struct goodix_ts_data {
u16 version;
bool reset_controller_at_probe;
bool load_cfg_from_disk;
int pen_input_registered;
struct completion firmware_loading_complete;
unsigned long irq_flags;
enum goodix_irq_pin_access_method irq_pin_access_method;

View File

@ -0,0 +1,367 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#define IST3038C_HIB_ACCESS (0x800B << 16)
#define IST3038C_DIRECT_ACCESS BIT(31)
#define IST3038C_REG_CHIPID 0x40001000
#define IST3038C_REG_HIB_BASE 0x30000100
#define IST3038C_REG_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS)
#define IST3038C_REG_TOUCH_COORD (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8)
#define IST3038C_REG_INTR_MESSAGE (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x4)
#define IST3038C_WHOAMI 0x38c
#define IST3038C_CHIP_ON_DELAY_MS 60
#define IST3038C_I2C_RETRY_COUNT 3
#define IST3038C_MAX_FINGER_NUM 10
#define IST3038C_X_MASK GENMASK(23, 12)
#define IST3038C_X_SHIFT 12
#define IST3038C_Y_MASK GENMASK(11, 0)
#define IST3038C_AREA_MASK GENMASK(27, 24)
#define IST3038C_AREA_SHIFT 24
#define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12)
#define IST3038C_FINGER_COUNT_SHIFT 12
#define IST3038C_FINGER_STATUS_MASK GENMASK(9, 0)
struct imagis_ts {
struct i2c_client *client;
struct input_dev *input_dev;
struct touchscreen_properties prop;
struct regulator_bulk_data supplies[2];
};
static int imagis_i2c_read_reg(struct imagis_ts *ts,
unsigned int reg, u32 *data)
{
__be32 ret_be;
__be32 reg_be = cpu_to_be32(reg);
struct i2c_msg msg[] = {
{
.addr = ts->client->addr,
.flags = 0,
.buf = (unsigned char *)&reg_be,
.len = sizeof(reg_be),
}, {
.addr = ts->client->addr,
.flags = I2C_M_RD,
.buf = (unsigned char *)&ret_be,
.len = sizeof(ret_be),
},
};
int ret, error;
int retry = IST3038C_I2C_RETRY_COUNT;
/* Retry in case the controller fails to respond */
do {
ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
if (ret == ARRAY_SIZE(msg)) {
*data = be32_to_cpu(ret_be);
return 0;
}
error = ret < 0 ? ret : -EIO;
dev_err(&ts->client->dev,
"%s - i2c_transfer failed: %d (%d)\n",
__func__, error, ret);
} while (--retry);
return error;
}
static irqreturn_t imagis_interrupt(int irq, void *dev_id)
{
struct imagis_ts *ts = dev_id;
u32 intr_message, finger_status;
unsigned int finger_count, finger_pressed;
int i;
int error;
error = imagis_i2c_read_reg(ts, IST3038C_REG_INTR_MESSAGE,
&intr_message);
if (error) {
dev_err(&ts->client->dev,
"failed to read the interrupt message: %d\n", error);
goto out;
}
finger_count = (intr_message & IST3038C_FINGER_COUNT_MASK) >>
IST3038C_FINGER_COUNT_SHIFT;
if (finger_count > IST3038C_MAX_FINGER_NUM) {
dev_err(&ts->client->dev,
"finger count %d is more than maximum supported\n",
finger_count);
goto out;
}
finger_pressed = intr_message & IST3038C_FINGER_STATUS_MASK;
for (i = 0; i < finger_count; i++) {
error = imagis_i2c_read_reg(ts,
IST3038C_REG_TOUCH_COORD + (i * 4),
&finger_status);
if (error) {
dev_err(&ts->client->dev,
"failed to read coordinates for finger %d: %d\n",
i, error);
goto out;
}
input_mt_slot(ts->input_dev, i);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
finger_pressed & BIT(i));
touchscreen_report_pos(ts->input_dev, &ts->prop,
(finger_status & IST3038C_X_MASK) >>
IST3038C_X_SHIFT,
finger_status & IST3038C_Y_MASK, 1);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
(finger_status & IST3038C_AREA_MASK) >>
IST3038C_AREA_SHIFT);
}
input_mt_sync_frame(ts->input_dev);
input_sync(ts->input_dev);
out:
return IRQ_HANDLED;
}
static void imagis_power_off(void *_ts)
{
struct imagis_ts *ts = _ts;
regulator_bulk_disable(ARRAY_SIZE(ts->supplies), ts->supplies);
}
static int imagis_power_on(struct imagis_ts *ts)
{
int error;
error = regulator_bulk_enable(ARRAY_SIZE(ts->supplies), ts->supplies);
if (error)
return error;
msleep(IST3038C_CHIP_ON_DELAY_MS);
return 0;
}
static int imagis_start(struct imagis_ts *ts)
{
int error;
error = imagis_power_on(ts);
if (error)
return error;
enable_irq(ts->client->irq);
return 0;
}
static int imagis_stop(struct imagis_ts *ts)
{
disable_irq(ts->client->irq);
imagis_power_off(ts);
return 0;
}
static int imagis_input_open(struct input_dev *dev)
{
struct imagis_ts *ts = input_get_drvdata(dev);
return imagis_start(ts);
}
static void imagis_input_close(struct input_dev *dev)
{
struct imagis_ts *ts = input_get_drvdata(dev);
imagis_stop(ts);
}
static int imagis_init_input_dev(struct imagis_ts *ts)
{
struct input_dev *input_dev;
int error;
input_dev = devm_input_allocate_device(&ts->client->dev);
if (!input_dev)
return -ENOMEM;
ts->input_dev = input_dev;
input_dev->name = "Imagis capacitive touchscreen";
input_dev->phys = "input/ts";
input_dev->id.bustype = BUS_I2C;
input_dev->open = imagis_input_open;
input_dev->close = imagis_input_close;
input_set_drvdata(input_dev, ts);
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
touchscreen_parse_properties(input_dev, true, &ts->prop);
if (!ts->prop.max_x || !ts->prop.max_y) {
dev_err(&ts->client->dev,
"Touchscreen-size-x and/or touchscreen-size-y not set in dts\n");
return -EINVAL;
}
error = input_mt_init_slots(input_dev,
IST3038C_MAX_FINGER_NUM,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(&ts->client->dev,
"Failed to initialize MT slots: %d", error);
return error;
}
error = input_register_device(input_dev);
if (error) {
dev_err(&ts->client->dev,
"Failed to register input device: %d", error);
return error;
}
return 0;
}
static int imagis_init_regulators(struct imagis_ts *ts)
{
struct i2c_client *client = ts->client;
ts->supplies[0].supply = "vdd";
ts->supplies[1].supply = "vddio";
return devm_regulator_bulk_get(&client->dev,
ARRAY_SIZE(ts->supplies),
ts->supplies);
}
static int imagis_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct imagis_ts *ts;
int chip_id, error;
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
ts->client = i2c;
error = imagis_init_regulators(ts);
if (error) {
dev_err(dev, "regulator init error: %d\n", error);
return error;
}
error = imagis_power_on(ts);
if (error) {
dev_err(dev, "failed to enable regulators: %d\n", error);
return error;
}
error = devm_add_action_or_reset(dev, imagis_power_off, ts);
if (error) {
dev_err(dev, "failed to install poweroff action: %d\n", error);
return error;
}
error = imagis_i2c_read_reg(ts,
IST3038C_REG_CHIPID | IST3038C_DIRECT_ACCESS,
&chip_id);
if (error) {
dev_err(dev, "chip ID read failure: %d\n", error);
return error;
}
if (chip_id != IST3038C_WHOAMI) {
dev_err(dev, "unknown chip ID: 0x%x\n", chip_id);
return -EINVAL;
}
error = devm_request_threaded_irq(dev, i2c->irq,
NULL, imagis_interrupt,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
"imagis-touchscreen", ts);
if (error) {
dev_err(dev, "IRQ %d allocation failure: %d\n",
i2c->irq, error);
return error;
}
error = imagis_init_input_dev(ts);
if (error)
return error;
return 0;
}
static int __maybe_unused imagis_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct imagis_ts *ts = i2c_get_clientdata(client);
int retval = 0;
mutex_lock(&ts->input_dev->mutex);
if (input_device_enabled(ts->input_dev))
retval = imagis_stop(ts);
mutex_unlock(&ts->input_dev->mutex);
return retval;
}
static int __maybe_unused imagis_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct imagis_ts *ts = i2c_get_clientdata(client);
int retval = 0;
mutex_lock(&ts->input_dev->mutex);
if (input_device_enabled(ts->input_dev))
retval = imagis_start(ts);
mutex_unlock(&ts->input_dev->mutex);
return retval;
}
static SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume);
#ifdef CONFIG_OF
static const struct of_device_id imagis_of_match[] = {
{ .compatible = "imagis,ist3038c", },
{ },
};
MODULE_DEVICE_TABLE(of, imagis_of_match);
#endif
static struct i2c_driver imagis_ts_driver = {
.driver = {
.name = "imagis-touchscreen",
.pm = &imagis_pm_ops,
.of_match_table = of_match_ptr(imagis_of_match),
},
.probe_new = imagis_probe,
};
module_i2c_driver(imagis_ts_driver);
MODULE_DESCRIPTION("Imagis IST3038C Touchscreen Driver");
MODULE_AUTHOR("Markuss Broks <markuss.broks@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -486,11 +486,11 @@ static int iqs5xx_axis_init(struct i2c_client *client)
{
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
struct touchscreen_properties *prop = &iqs5xx->prop;
struct input_dev *input;
struct input_dev *input = iqs5xx->input;
u16 max_x, max_y;
int error;
if (!iqs5xx->input) {
if (!input) {
input = devm_input_allocate_device(&client->dev);
if (!input)
return -ENOMEM;
@ -512,11 +512,11 @@ static int iqs5xx_axis_init(struct i2c_client *client)
if (error)
return error;
input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
input_set_abs_params(iqs5xx->input, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
input_set_abs_params(input, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
touchscreen_parse_properties(iqs5xx->input, true, prop);
touchscreen_parse_properties(input, true, prop);
/*
* The device reserves 0xFFFF for coordinates that correspond to slots
@ -540,7 +540,7 @@ static int iqs5xx_axis_init(struct i2c_client *client)
return error;
}
error = input_mt_init_slots(iqs5xx->input, IQS5XX_NUM_CONTACTS,
error = input_mt_init_slots(input, IQS5XX_NUM_CONTACTS,
INPUT_MT_DIRECT);
if (error)
dev_err(&client->dev, "Failed to initialize slots: %d\n",
@ -674,7 +674,7 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
input_mt_slot(input, i);
if (input_mt_report_slot_state(input, MT_TOOL_FINGER,
pressure != 0)) {
touchscreen_report_pos(iqs5xx->input, &iqs5xx->prop,
touchscreen_report_pos(input, &iqs5xx->prop,
be16_to_cpu(touch_data->abs_x),
be16_to_cpu(touch_data->abs_y),
true);

View File

@ -339,11 +339,11 @@ static int stmfts_input_open(struct input_dev *dev)
err = pm_runtime_get_sync(&sdata->client->dev);
if (err < 0)
return err;
goto out;
err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON);
if (err)
return err;
goto out;
mutex_lock(&sdata->mutex);
sdata->running = true;
@ -366,7 +366,9 @@ static int stmfts_input_open(struct input_dev *dev)
"failed to enable touchkey\n");
}
return 0;
out:
pm_runtime_put_noidle(&sdata->client->dev);
return err;
}
static void stmfts_input_close(struct input_dev *dev)

View File

@ -88,6 +88,8 @@ struct tsc200x {
int in_z1;
int in_z2;
struct touchscreen_properties prop;
spinlock_t lock;
struct timer_list penup_timer;
@ -113,8 +115,7 @@ static void tsc200x_update_pen_state(struct tsc200x *ts,
int x, int y, int pressure)
{
if (pressure) {
input_report_abs(ts->idev, ABS_X, x);
input_report_abs(ts->idev, ABS_Y, y);
touchscreen_report_pos(ts->idev, &ts->prop, x, y, false);
input_report_abs(ts->idev, ABS_PRESSURE, pressure);
if (!ts->pen_down) {
input_report_key(ts->idev, BTN_TOUCH, !!pressure);
@ -533,7 +534,7 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
input_set_abs_params(input_dev, ABS_PRESSURE,
0, MAX_12BIT, TSC200X_DEF_P_FUZZ, 0);
touchscreen_parse_properties(input_dev, false, NULL);
touchscreen_parse_properties(input_dev, false, &ts->prop);
/* Ensure the touchscreen is off */
tsc200x_stop_scan(ts);

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Helpers for ChromeOS Vivaldi keyboard function row mapping
*
* Copyright (C) 2022 Google, Inc
*/
#include <linux/export.h>
#include <linux/input/vivaldi-fmap.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
/**
* vivaldi_function_row_physmap_show - Print vivaldi function row physmap attribute
* @data: The vivaldi function row map
* @buf: Buffer to print the function row phsymap to
*/
ssize_t vivaldi_function_row_physmap_show(const struct vivaldi_data *data,
char *buf)
{
ssize_t size = 0;
int i;
const u32 *physmap = data->function_row_physmap;
if (!data->num_function_row_keys)
return 0;
for (i = 0; i < data->num_function_row_keys; i++)
size += scnprintf(buf + size, PAGE_SIZE - size,
"%s%02X", size ? " " : "", physmap[i]);
if (size)
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
return size;
}
EXPORT_SYMBOL_GPL(vivaldi_function_row_physmap_show);
MODULE_LICENSE("GPL");

View File

@ -475,6 +475,8 @@ static inline void input_set_events_per_packet(struct input_dev *dev, int n_even
void input_alloc_absinfo(struct input_dev *dev);
void input_set_abs_params(struct input_dev *dev, unsigned int axis,
int min, int max, int fuzz, int flat);
void input_copy_abs(struct input_dev *dst, unsigned int dst_axis,
const struct input_dev *src, unsigned int src_axis);
#define INPUT_GENERATE_ABS_ACCESSORS(_suffix, _item) \
static inline int input_abs_get_##_suffix(struct input_dev *dev, \

View File

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _VIVALDI_FMAP_H
#define _VIVALDI_FMAP_H
#include <linux/types.h>
#define VIVALDI_MAX_FUNCTION_ROW_KEYS 24
/**
* struct vivaldi_data - Function row map data for ChromeOS Vivaldi keyboards
* @function_row_physmap: An array of scancodes or their equivalent (HID usage
* codes, encoded rows/columns, etc) for the top
* row function keys, in an order from left to right
* @num_function_row_keys: The number of top row keys in a custom keyboard
*
* This structure is supposed to be used by ChromeOS keyboards using
* the Vivaldi keyboard function row design.
*/
struct vivaldi_data {
u32 function_row_physmap[VIVALDI_MAX_FUNCTION_ROW_KEYS];
unsigned int num_function_row_keys;
};
ssize_t vivaldi_function_row_physmap_show(const struct vivaldi_data *data,
char *buf);
#endif /* _VIVALDI_FMAP_H */