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:
commit
aa240ee788
|
@ -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";
|
||||
};
|
||||
};
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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,.*":
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 @@ out:
|
|||
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,
|
||||
};
|
||||
|
|
|
@ -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");
|
|
@ -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 */
|
|
@ -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) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *)®_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");
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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, \
|
||||
|
|
|
@ -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 */
|
Loading…
Reference in New Issue