Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: - a new driver for ADC driven joysticks - a new Zintix touchscreen driver - enhancements to Intel SoC button array driver - support for F3A "function" in Synaptics RMI4 driver - assorted driver fixups * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (29 commits) Input: Add MAINTAINERS entry for SiS i2c touch input driver Input: evdev - per-client waitgroups Input: synaptics - enable InterTouch for ThinkPad T14 Gen 1 Input: synaptics - enable InterTouch for ThinkPad P1/X1E gen 2 Input: synaptics-rmi4 - support bootloader v8 in f34v7 Input: synaptics-rmi4 - add support for F3A Input: synaptics-rmi4 - rename f30_data to gpio_data Input: add zinitix touchscreen driver dt-bindings: input/touchscreen: add bindings for zinitix Input: joystick - add ADC attached joystick driver. dt-bindings: input: Add docs for ADC driven joystick Input: sun4i-ps2 - fix handling of platform_get_irq() error Input: twl4030_keypad - fix handling of platform_get_irq() error Input: omap4-keypad - fix handling of platform_get_irq() error Input: ep93xx_keypad - fix handling of platform_get_irq() error Input: stmfts - fix a & vs && typo Input: imx6ul_tsc - unify open/close and PM paths Input: imx6ul_tsc - clean up some errors in imx6ul_tsc_resume() Input: elants_i2c - fix typo for an attribute to show calibration count Input: elants_i2c - report resolution of ABS_MT_TOUCH_MAJOR by FW information. ...
This commit is contained in:
Коммит
bd7e8c996f
|
@ -0,0 +1,121 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2019-2020 Artur Rojek
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/input/adc-joystick.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: ADC attached joystick
|
||||
|
||||
maintainers:
|
||||
- Artur Rojek <contact@artur-rojek.eu>
|
||||
|
||||
description: >
|
||||
Bindings for joystick devices connected to ADC controllers supporting
|
||||
the Industrial I/O subsystem.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: adc-joystick
|
||||
|
||||
io-channels:
|
||||
minItems: 1
|
||||
maxItems: 1024
|
||||
description: >
|
||||
List of phandle and IIO specifier pairs.
|
||||
Each pair defines one ADC channel to which a joystick axis is connected.
|
||||
See Documentation/devicetree/bindings/iio/iio-bindings.txt for details.
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- io-channels
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
patternProperties:
|
||||
"^axis@[0-9a-f]+$":
|
||||
type: object
|
||||
description: >
|
||||
Represents a joystick axis bound to the given ADC channel.
|
||||
For each entry in the io-channels list, one axis subnode with a matching
|
||||
reg property must be specified.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 1023
|
||||
description: Index of an io-channels list entry bound to this axis.
|
||||
|
||||
linux,code:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: EV_ABS specific event code generated by the axis.
|
||||
|
||||
abs-range:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- items:
|
||||
- description: minimum value
|
||||
- description: maximum value
|
||||
description: >
|
||||
Minimum and maximum values produced by the axis.
|
||||
For an ABS_X axis this will be the left-most and right-most
|
||||
inclination of the joystick. If min > max, it is left to userspace to
|
||||
treat the axis as inverted.
|
||||
This property is interpreted as two signed 32 bit values.
|
||||
|
||||
abs-fuzz:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: >
|
||||
Amount of noise in the input value.
|
||||
Omitting this property indicates the axis is precise.
|
||||
|
||||
abs-flat:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: >
|
||||
Axial "deadzone", or area around the center position, where the axis
|
||||
is considered to be at rest.
|
||||
Omitting this property indicates the axis always returns to exactly
|
||||
the center position.
|
||||
|
||||
required:
|
||||
- reg
|
||||
- linux,code
|
||||
- abs-range
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/iio/adc/ingenic,adc.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
|
||||
joystick: adc-joystick {
|
||||
compatible = "adc-joystick";
|
||||
io-channels = <&adc INGENIC_ADC_TOUCH_XP>,
|
||||
<&adc INGENIC_ADC_TOUCH_YP>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
axis@0 {
|
||||
reg = <0>;
|
||||
linux,code = <ABS_X>;
|
||||
abs-range = <3300 0>;
|
||||
abs-fuzz = <4>;
|
||||
abs-flat = <200>;
|
||||
};
|
||||
axis@1 {
|
||||
reg = <1>;
|
||||
linux,code = <ABS_Y>;
|
||||
abs-range = <0 3300>;
|
||||
abs-fuzz = <4>;
|
||||
abs-flat = <200>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
Device tree bindings for Zinitx BT541 touchscreen controller
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Should be "zinitix,bt541"
|
||||
- reg : I2C address of the chip. Should be 0x20
|
||||
- interrupts : Interrupt to which the chip is connected
|
||||
|
||||
Optional properties:
|
||||
|
||||
- vdd-supply : Analog power supply regulator on VCCA pin
|
||||
- vddo-supply : Digital power supply regulator on VDD pin
|
||||
- zinitix,mode : Mode of reporting touch points. Some modes may not work
|
||||
with a particular ts firmware for unknown reasons. Available
|
||||
modes are 1 and 2. Mode 2 is the default and preferred.
|
||||
|
||||
The touchscreen-* properties are documented in touchscreen.txt in this
|
||||
directory.
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
/* ... */
|
||||
|
||||
bt541@20 {
|
||||
compatible = "zinitix,bt541";
|
||||
reg = <0x20>;
|
||||
interrupt-parent = <&msmgpio>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&tsp_default>;
|
||||
vdd-supply = <®_vdd_tsp>;
|
||||
vddo-supply = <&pm8916_l6>;
|
||||
touchscreen-size-x = <540>;
|
||||
touchscreen-size-y = <960>;
|
||||
zinitix,mode = <2>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
|
@ -1222,6 +1222,8 @@ patternProperties:
|
|||
description: Shenzhen Zidoo Technology Co., Ltd.
|
||||
"^zii,.*":
|
||||
description: Zodiac Inflight Innovations
|
||||
"^zinitix,.*":
|
||||
description: Zinitix Co., Ltd
|
||||
"^zte,.*":
|
||||
description: ZTE Corp.
|
||||
"^zyxel,.*":
|
||||
|
|
|
@ -16044,6 +16044,13 @@ F: Documentation/fb/sisfb.rst
|
|||
F: drivers/video/fbdev/sis/
|
||||
F: include/video/sisfb.h
|
||||
|
||||
SIS I2C TOUCHSCREEN DRIVER
|
||||
M: Mika Penttilä <mika.penttila@nextfour.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/input/touchscreen/sis_i2c.txt
|
||||
F: drivers/input/touchscreen/sis_i2c.c
|
||||
|
||||
SIS USB2VGA DRIVER
|
||||
M: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
S: Maintained
|
||||
|
|
|
@ -720,7 +720,7 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
}
|
||||
|
||||
if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)
|
||||
rmi_hid_pdata.f30_data.disable = true;
|
||||
rmi_hid_pdata.gpio_data.disable = true;
|
||||
|
||||
data->xport.dev = hdev->dev.parent;
|
||||
data->xport.pdata = rmi_hid_pdata;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
struct evdev {
|
||||
int open;
|
||||
struct input_handle handle;
|
||||
wait_queue_head_t wait;
|
||||
struct evdev_client __rcu *grab;
|
||||
struct list_head client_list;
|
||||
spinlock_t client_lock; /* protects client_list */
|
||||
|
@ -43,6 +42,7 @@ struct evdev_client {
|
|||
unsigned int tail;
|
||||
unsigned int packet_head; /* [future] position of the first element of next packet */
|
||||
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
||||
wait_queue_head_t wait;
|
||||
struct fasync_struct *fasync;
|
||||
struct evdev *evdev;
|
||||
struct list_head node;
|
||||
|
@ -245,7 +245,6 @@ static void evdev_pass_values(struct evdev_client *client,
|
|||
const struct input_value *vals, unsigned int count,
|
||||
ktime_t *ev_time)
|
||||
{
|
||||
struct evdev *evdev = client->evdev;
|
||||
const struct input_value *v;
|
||||
struct input_event event;
|
||||
struct timespec64 ts;
|
||||
|
@ -282,7 +281,7 @@ static void evdev_pass_values(struct evdev_client *client,
|
|||
spin_unlock(&client->buffer_lock);
|
||||
|
||||
if (wakeup)
|
||||
wake_up_interruptible_poll(&evdev->wait,
|
||||
wake_up_interruptible_poll(&client->wait,
|
||||
EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM);
|
||||
}
|
||||
|
||||
|
@ -426,11 +425,11 @@ static void evdev_hangup(struct evdev *evdev)
|
|||
struct evdev_client *client;
|
||||
|
||||
spin_lock(&evdev->client_lock);
|
||||
list_for_each_entry(client, &evdev->client_list, node)
|
||||
list_for_each_entry(client, &evdev->client_list, node) {
|
||||
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
||||
wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR);
|
||||
}
|
||||
spin_unlock(&evdev->client_lock);
|
||||
|
||||
wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR);
|
||||
}
|
||||
|
||||
static int evdev_release(struct inode *inode, struct file *file)
|
||||
|
@ -479,6 +478,7 @@ static int evdev_open(struct inode *inode, struct file *file)
|
|||
if (!client)
|
||||
return -ENOMEM;
|
||||
|
||||
init_waitqueue_head(&client->wait);
|
||||
client->bufsize = bufsize;
|
||||
spin_lock_init(&client->buffer_lock);
|
||||
client->evdev = evdev;
|
||||
|
@ -595,7 +595,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
|
|||
break;
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK)) {
|
||||
error = wait_event_interruptible(evdev->wait,
|
||||
error = wait_event_interruptible(client->wait,
|
||||
client->packet_head != client->tail ||
|
||||
!evdev->exist || client->revoked);
|
||||
if (error)
|
||||
|
@ -613,7 +613,7 @@ static __poll_t evdev_poll(struct file *file, poll_table *wait)
|
|||
struct evdev *evdev = client->evdev;
|
||||
__poll_t mask;
|
||||
|
||||
poll_wait(file, &evdev->wait, wait);
|
||||
poll_wait(file, &client->wait, wait);
|
||||
|
||||
if (evdev->exist && !client->revoked)
|
||||
mask = EPOLLOUT | EPOLLWRNORM;
|
||||
|
@ -946,7 +946,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
|
|||
client->revoked = true;
|
||||
evdev_ungrab(evdev, client);
|
||||
input_flush_device(&evdev->handle, file);
|
||||
wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR);
|
||||
wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1358,7 +1358,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
|||
INIT_LIST_HEAD(&evdev->client_list);
|
||||
spin_lock_init(&evdev->client_lock);
|
||||
mutex_init(&evdev->mutex);
|
||||
init_waitqueue_head(&evdev->wait);
|
||||
evdev->exist = true;
|
||||
|
||||
dev_no = minor;
|
||||
|
|
|
@ -323,11 +323,14 @@ static int adjust_dual(int *begin, int step, int *end, int eq, int mu)
|
|||
p = begin + step;
|
||||
s = p == end ? f + 1 : *p;
|
||||
|
||||
for (; p != end; p += step)
|
||||
if (*p < f)
|
||||
s = f, f = *p;
|
||||
else if (*p < s)
|
||||
for (; p != end; p += step) {
|
||||
if (*p < f) {
|
||||
s = f;
|
||||
f = *p;
|
||||
} else if (*p < s) {
|
||||
s = *p;
|
||||
}
|
||||
}
|
||||
|
||||
c = (f + s + 1) / 2;
|
||||
if (c == 0 || (c > mu && (!eq || mu > 0)))
|
||||
|
|
|
@ -42,6 +42,16 @@ config JOYSTICK_A3D
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called a3d.
|
||||
|
||||
config JOYSTICK_ADC
|
||||
tristate "Simple joystick connected over ADC"
|
||||
depends on IIO
|
||||
select IIO_BUFFER_CB
|
||||
help
|
||||
Say Y here if you have a simple joystick connected over ADC.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adc-joystick.
|
||||
|
||||
config JOYSTICK_ADI
|
||||
tristate "Logitech ADI digital joysticks and gamepads"
|
||||
select GAMEPORT
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
|
||||
obj-$(CONFIG_JOYSTICK_ADC) += adc-joystick.o
|
||||
obj-$(CONFIG_JOYSTICK_ADI) += adi.o
|
||||
obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
|
||||
obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Input driver for joysticks connected over ADC.
|
||||
* Copyright (c) 2019-2020 Artur Rojek <contact@artur-rojek.eu>
|
||||
*/
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
struct adc_joystick_axis {
|
||||
u32 code;
|
||||
s32 range[2];
|
||||
s32 fuzz;
|
||||
s32 flat;
|
||||
};
|
||||
|
||||
struct adc_joystick {
|
||||
struct input_dev *input;
|
||||
struct iio_cb_buffer *buffer;
|
||||
struct adc_joystick_axis *axes;
|
||||
struct iio_channel *chans;
|
||||
int num_chans;
|
||||
};
|
||||
|
||||
static int adc_joystick_handle(const void *data, void *private)
|
||||
{
|
||||
struct adc_joystick *joy = private;
|
||||
enum iio_endian endianness;
|
||||
int bytes, msb, val, idx, i;
|
||||
const u16 *data_u16;
|
||||
bool sign;
|
||||
|
||||
bytes = joy->chans[0].channel->scan_type.storagebits >> 3;
|
||||
|
||||
for (i = 0; i < joy->num_chans; ++i) {
|
||||
idx = joy->chans[i].channel->scan_index;
|
||||
endianness = joy->chans[i].channel->scan_type.endianness;
|
||||
msb = joy->chans[i].channel->scan_type.realbits - 1;
|
||||
sign = tolower(joy->chans[i].channel->scan_type.sign) == 's';
|
||||
|
||||
switch (bytes) {
|
||||
case 1:
|
||||
val = ((const u8 *)data)[idx];
|
||||
break;
|
||||
case 2:
|
||||
data_u16 = (const u16 *)data + idx;
|
||||
|
||||
/*
|
||||
* Data is aligned to the sample size by IIO core.
|
||||
* Call `get_unaligned_xe16` to hide type casting.
|
||||
*/
|
||||
if (endianness == IIO_BE)
|
||||
val = get_unaligned_be16(data_u16);
|
||||
else if (endianness == IIO_LE)
|
||||
val = get_unaligned_le16(data_u16);
|
||||
else /* IIO_CPU */
|
||||
val = *data_u16;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val >>= joy->chans[i].channel->scan_type.shift;
|
||||
if (sign)
|
||||
val = sign_extend32(val, msb);
|
||||
else
|
||||
val &= GENMASK(msb, 0);
|
||||
input_report_abs(joy->input, joy->axes[i].code, val);
|
||||
}
|
||||
|
||||
input_sync(joy->input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_joystick_open(struct input_dev *dev)
|
||||
{
|
||||
struct adc_joystick *joy = input_get_drvdata(dev);
|
||||
struct device *devp = &dev->dev;
|
||||
int ret;
|
||||
|
||||
ret = iio_channel_start_all_cb(joy->buffer);
|
||||
if (ret)
|
||||
dev_err(devp, "Unable to start callback buffer: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void adc_joystick_close(struct input_dev *dev)
|
||||
{
|
||||
struct adc_joystick *joy = input_get_drvdata(dev);
|
||||
|
||||
iio_channel_stop_all_cb(joy->buffer);
|
||||
}
|
||||
|
||||
static void adc_joystick_cleanup(void *data)
|
||||
{
|
||||
iio_channel_release_all_cb(data);
|
||||
}
|
||||
|
||||
static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
|
||||
{
|
||||
struct adc_joystick_axis *axes;
|
||||
struct fwnode_handle *child;
|
||||
int num_axes, error, i;
|
||||
|
||||
num_axes = device_get_child_node_count(dev);
|
||||
if (!num_axes) {
|
||||
dev_err(dev, "Unable to find child nodes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (num_axes != joy->num_chans) {
|
||||
dev_err(dev, "Got %d child nodes for %d channels\n",
|
||||
num_axes, joy->num_chans);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
axes = devm_kmalloc_array(dev, num_axes, sizeof(*axes), GFP_KERNEL);
|
||||
if (!axes)
|
||||
return -ENOMEM;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
error = fwnode_property_read_u32(child, "reg", &i);
|
||||
if (error) {
|
||||
dev_err(dev, "reg invalid or missing\n");
|
||||
goto err_fwnode_put;
|
||||
}
|
||||
|
||||
if (i >= num_axes) {
|
||||
error = -EINVAL;
|
||||
dev_err(dev, "No matching axis for reg %d\n", i);
|
||||
goto err_fwnode_put;
|
||||
}
|
||||
|
||||
error = fwnode_property_read_u32(child, "linux,code",
|
||||
&axes[i].code);
|
||||
if (error) {
|
||||
dev_err(dev, "linux,code invalid or missing\n");
|
||||
goto err_fwnode_put;
|
||||
}
|
||||
|
||||
error = fwnode_property_read_u32_array(child, "abs-range",
|
||||
axes[i].range, 2);
|
||||
if (error) {
|
||||
dev_err(dev, "abs-range invalid or missing\n");
|
||||
goto err_fwnode_put;
|
||||
}
|
||||
|
||||
fwnode_property_read_u32(child, "abs-fuzz", &axes[i].fuzz);
|
||||
fwnode_property_read_u32(child, "abs-flat", &axes[i].flat);
|
||||
|
||||
input_set_abs_params(joy->input, axes[i].code,
|
||||
axes[i].range[0], axes[i].range[1],
|
||||
axes[i].fuzz, axes[i].flat);
|
||||
input_set_capability(joy->input, EV_ABS, axes[i].code);
|
||||
}
|
||||
|
||||
joy->axes = axes;
|
||||
|
||||
return 0;
|
||||
|
||||
err_fwnode_put:
|
||||
fwnode_handle_put(child);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int adc_joystick_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct adc_joystick *joy;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
int bits;
|
||||
int i;
|
||||
|
||||
joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
|
||||
if (!joy)
|
||||
return -ENOMEM;
|
||||
|
||||
joy->chans = devm_iio_channel_get_all(dev);
|
||||
if (IS_ERR(joy->chans)) {
|
||||
error = PTR_ERR(joy->chans);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "Unable to get IIO channels");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Count how many channels we got. NULL terminated. */
|
||||
for (i = 0; joy->chans[i].indio_dev; i++) {
|
||||
bits = joy->chans[i].channel->scan_type.storagebits;
|
||||
if (!bits || bits > 16) {
|
||||
dev_err(dev, "Unsupported channel storage size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bits != joy->chans[0].channel->scan_type.storagebits) {
|
||||
dev_err(dev, "Channels must have equal storage size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
joy->num_chans = i;
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input) {
|
||||
dev_err(dev, "Unable to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
joy->input = input;
|
||||
input->name = pdev->name;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->open = adc_joystick_open;
|
||||
input->close = adc_joystick_close;
|
||||
|
||||
error = adc_joystick_set_axes(dev, joy);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_set_drvdata(input, joy);
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy);
|
||||
if (IS_ERR(joy->buffer)) {
|
||||
dev_err(dev, "Unable to allocate callback buffer\n");
|
||||
return PTR_ERR(joy->buffer);
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(dev, adc_joystick_cleanup, joy->buffer);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to add action\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id adc_joystick_of_match[] = {
|
||||
{ .compatible = "adc-joystick", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc_joystick_of_match);
|
||||
|
||||
static struct platform_driver adc_joystick_driver = {
|
||||
.driver = {
|
||||
.name = "adc-joystick",
|
||||
.of_match_table = adc_joystick_of_match,
|
||||
},
|
||||
.probe = adc_joystick_probe,
|
||||
};
|
||||
module_platform_driver(adc_joystick_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Input driver for joysticks connected over ADC");
|
||||
MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -250,8 +250,8 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
keypad->irq = platform_get_irq(pdev, 0);
|
||||
if (!keypad->irq) {
|
||||
err = -ENXIO;
|
||||
if (keypad->irq < 0) {
|
||||
err = keypad->irq;
|
||||
goto failed_free;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,10 +240,8 @@ static int omap4_keypad_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "no keyboard irq assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
|
||||
if (!keypad_data) {
|
||||
|
|
|
@ -50,7 +50,7 @@ struct twl4030_keypad {
|
|||
bool autorepeat;
|
||||
unsigned int n_rows;
|
||||
unsigned int n_cols;
|
||||
unsigned int irq;
|
||||
int irq;
|
||||
|
||||
struct device *dbg_dev;
|
||||
struct input_dev *input;
|
||||
|
@ -376,10 +376,8 @@ static int twl4030_kp_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
kp->irq = platform_get_irq(pdev, 0);
|
||||
if (!kp->irq) {
|
||||
dev_err(&pdev->dev, "no keyboard irq assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (kp->irq < 0)
|
||||
return kp->irq;
|
||||
|
||||
error = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
TWL4030_MAX_ROWS,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -23,6 +24,7 @@ struct soc_button_info {
|
|||
unsigned int event_code;
|
||||
bool autorepeat;
|
||||
bool wakeup;
|
||||
bool active_low;
|
||||
};
|
||||
|
||||
struct soc_device_data {
|
||||
|
@ -41,23 +43,66 @@ struct soc_button_data {
|
|||
struct platform_device *children[BUTTON_TYPES];
|
||||
};
|
||||
|
||||
/*
|
||||
* Some 2-in-1s which use the soc_button_array driver have this ugly issue in
|
||||
* their DSDT where the _LID method modifies the irq-type settings of the GPIOs
|
||||
* used for the power and home buttons. The intend of this AML code is to
|
||||
* disable these buttons when the lid is closed.
|
||||
* The AML does this by directly poking the GPIO controllers registers. This is
|
||||
* problematic because when re-enabling the irq, which happens whenever _LID
|
||||
* gets called with the lid open (e.g. on boot and on resume), it sets the
|
||||
* irq-type to IRQ_TYPE_LEVEL_LOW. Where as the gpio-keys driver programs the
|
||||
* type to, and expects it to be, IRQ_TYPE_EDGE_BOTH.
|
||||
* To work around this we don't set gpio_keys_button.gpio on these 2-in-1s,
|
||||
* instead we get the irq for the GPIO ourselves, configure it as
|
||||
* IRQ_TYPE_LEVEL_LOW (to match how the _LID AML code configures it) and pass
|
||||
* the irq in gpio_keys_button.irq. Below is a list of affected devices.
|
||||
*/
|
||||
static const struct dmi_system_id dmi_use_low_level_irq[] = {
|
||||
{
|
||||
/*
|
||||
* Acer Switch 10 SW5-012. _LID method messes with home- and
|
||||
* power-button GPIO IRQ settings. When (re-)enabling the irq
|
||||
* it ors in its own flags without clearing the previous set
|
||||
* ones, leading to an irq-type of IRQ_TYPE_LEVEL_LOW |
|
||||
* IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Acer One S1003. _LID method messes with power-button GPIO
|
||||
* IRQ settings, leading to a non working power-button.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "One S1003"),
|
||||
},
|
||||
},
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the Nth GPIO number from the ACPI object.
|
||||
*/
|
||||
static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
|
||||
static int soc_button_lookup_gpio(struct device *dev, int acpi_index,
|
||||
int *gpio_ret, int *irq_ret)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
int gpio;
|
||||
|
||||
desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
gpio = desc_to_gpio(desc);
|
||||
*gpio_ret = desc_to_gpio(desc);
|
||||
*irq_ret = gpiod_to_irq(desc);
|
||||
|
||||
gpiod_put(desc);
|
||||
|
||||
return gpio;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device *
|
||||
|
@ -69,9 +114,8 @@ soc_button_device_create(struct platform_device *pdev,
|
|||
struct platform_device *pd;
|
||||
struct gpio_keys_button *gpio_keys;
|
||||
struct gpio_keys_platform_data *gpio_keys_pdata;
|
||||
int error, gpio, irq;
|
||||
int n_buttons = 0;
|
||||
int gpio;
|
||||
int error;
|
||||
|
||||
for (info = button_info; info->name; info++)
|
||||
if (info->autorepeat == autorepeat)
|
||||
|
@ -91,8 +135,8 @@ soc_button_device_create(struct platform_device *pdev,
|
|||
if (info->autorepeat != autorepeat)
|
||||
continue;
|
||||
|
||||
gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq);
|
||||
if (error || irq < 0) {
|
||||
/*
|
||||
* Skip GPIO if not present. Note we deliberately
|
||||
* ignore -EPROBE_DEFER errors here. On some devices
|
||||
|
@ -107,10 +151,18 @@ soc_button_device_create(struct platform_device *pdev,
|
|||
continue;
|
||||
}
|
||||
|
||||
/* See dmi_use_low_level_irq[] comment */
|
||||
if (!autorepeat && dmi_check_system(dmi_use_low_level_irq)) {
|
||||
irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
|
||||
gpio_keys[n_buttons].irq = irq;
|
||||
gpio_keys[n_buttons].gpio = -ENOENT;
|
||||
} else {
|
||||
gpio_keys[n_buttons].gpio = gpio;
|
||||
}
|
||||
|
||||
gpio_keys[n_buttons].type = info->event_type;
|
||||
gpio_keys[n_buttons].code = info->event_code;
|
||||
gpio_keys[n_buttons].gpio = gpio;
|
||||
gpio_keys[n_buttons].active_low = 1;
|
||||
gpio_keys[n_buttons].active_low = info->active_low;
|
||||
gpio_keys[n_buttons].desc = info->name;
|
||||
gpio_keys[n_buttons].wakeup = info->wakeup;
|
||||
/* These devices often use cheap buttons, use 50 ms debounce */
|
||||
|
@ -173,6 +225,7 @@ static int soc_button_parse_btn_desc(struct device *dev,
|
|||
}
|
||||
|
||||
info->event_type = EV_KEY;
|
||||
info->active_low = true;
|
||||
info->acpi_index =
|
||||
soc_button_get_acpi_object_int(&desc->package.elements[1]);
|
||||
upage = soc_button_get_acpi_object_int(&desc->package.elements[3]);
|
||||
|
@ -383,11 +436,11 @@ static int soc_button_probe(struct platform_device *pdev)
|
|||
* Platforms"
|
||||
*/
|
||||
static const struct soc_button_info soc_button_PNP0C40[] = {
|
||||
{ "power", 0, EV_KEY, KEY_POWER, false, true },
|
||||
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
|
||||
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
|
||||
{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
|
||||
{ "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false },
|
||||
{ "power", 0, EV_KEY, KEY_POWER, false, true, true },
|
||||
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true, true },
|
||||
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true },
|
||||
{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true },
|
||||
{ "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false, true },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -395,6 +448,15 @@ static const struct soc_device_data soc_device_PNP0C40 = {
|
|||
.button_info = soc_button_PNP0C40,
|
||||
};
|
||||
|
||||
static const struct soc_button_info soc_button_INT33D3[] = {
|
||||
{ "tablet_mode", 0, EV_SW, SW_TABLET_MODE, false, false, false },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct soc_device_data soc_device_INT33D3 = {
|
||||
.button_info = soc_button_INT33D3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Special device check for Surface Book 2 and Surface Pro (2017).
|
||||
* Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
|
||||
|
@ -444,9 +506,9 @@ static int soc_device_check_MSHW0040(struct device *dev)
|
|||
* Obtained from DSDT/testing.
|
||||
*/
|
||||
static const struct soc_button_info soc_button_MSHW0040[] = {
|
||||
{ "power", 0, EV_KEY, KEY_POWER, false, true },
|
||||
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
|
||||
{ "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false },
|
||||
{ "power", 0, EV_KEY, KEY_POWER, false, true, true },
|
||||
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true },
|
||||
{ "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false, true },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -457,6 +519,8 @@ static const struct soc_device_data soc_device_MSHW0040 = {
|
|||
|
||||
static const struct acpi_device_id soc_button_acpi_match[] = {
|
||||
{ "PNP0C40", (unsigned long)&soc_device_PNP0C40 },
|
||||
{ "INT33D3", (unsigned long)&soc_device_INT33D3 },
|
||||
{ "ID9001", (unsigned long)&soc_device_INT33D3 },
|
||||
{ "ACPI0011", 0 },
|
||||
|
||||
/* Microsoft Surface Devices (5th and 6th generation) */
|
||||
|
|
|
@ -179,12 +179,14 @@ static const char * const smbus_pnp_ids[] = {
|
|||
"LEN0093", /* T480 */
|
||||
"LEN0096", /* X280 */
|
||||
"LEN0097", /* X280 -> ALPS trackpoint */
|
||||
"LEN0099", /* X1 Extreme 1st */
|
||||
"LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
|
||||
"LEN009b", /* T580 */
|
||||
"LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
|
||||
"LEN200f", /* T450s */
|
||||
"LEN2044", /* L470 */
|
||||
"LEN2054", /* E480 */
|
||||
"LEN2055", /* E580 */
|
||||
"LEN2068", /* T14 Gen 1 */
|
||||
"SYN3052", /* HP EliteBook 840 G4 */
|
||||
"SYN3221", /* HP 15-ay000 */
|
||||
"SYN323d", /* HP Spectre X360 13-w013dx */
|
||||
|
@ -1752,7 +1754,7 @@ static int synaptics_create_intertouch(struct psmouse *psmouse,
|
|||
.kernel_tracking = false,
|
||||
.topbuttonpad = topbuttonpad,
|
||||
},
|
||||
.f30_data = {
|
||||
.gpio_data = {
|
||||
.buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c),
|
||||
.trackstick_buttons =
|
||||
!!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10),
|
||||
|
|
|
@ -100,6 +100,14 @@ config RMI4_F34
|
|||
device via the firmware loader interface. This is triggered using a
|
||||
sysfs attribute.
|
||||
|
||||
config RMI4_F3A
|
||||
bool "RMI4 Function 3A (GPIO)"
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 3A.
|
||||
|
||||
Function 3A provides GPIO support for RMI4 devices. This includes
|
||||
support for buttons on TouchPads and ClickPads.
|
||||
|
||||
config RMI4_F54
|
||||
bool "RMI4 Function 54 (Analog diagnostics)"
|
||||
depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m)
|
||||
|
|
|
@ -10,6 +10,7 @@ rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
|
|||
rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
|
||||
rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
|
||||
rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o
|
||||
rmi_core-$(CONFIG_RMI4_F3A) += rmi_f3a.o
|
||||
rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o
|
||||
rmi_core-$(CONFIG_RMI4_F55) += rmi_f55.o
|
||||
|
||||
|
|
|
@ -365,6 +365,9 @@ static struct rmi_function_handler *fn_handlers[] = {
|
|||
#ifdef CONFIG_RMI4_F34
|
||||
&rmi_f34_handler,
|
||||
#endif
|
||||
#ifdef CONFIG_RMI4_F3A
|
||||
&rmi_f3a_handler,
|
||||
#endif
|
||||
#ifdef CONFIG_RMI4_F54
|
||||
&rmi_f54_handler,
|
||||
#endif
|
||||
|
|
|
@ -135,6 +135,7 @@ extern struct rmi_function_handler rmi_f11_handler;
|
|||
extern struct rmi_function_handler rmi_f12_handler;
|
||||
extern struct rmi_function_handler rmi_f30_handler;
|
||||
extern struct rmi_function_handler rmi_f34_handler;
|
||||
extern struct rmi_function_handler rmi_f3a_handler;
|
||||
extern struct rmi_function_handler rmi_f54_handler;
|
||||
extern struct rmi_function_handler rmi_f55_handler;
|
||||
#endif
|
||||
|
|
|
@ -168,17 +168,17 @@ static int rmi_f30_config(struct rmi_function *fn)
|
|||
rmi_get_platform_data(fn->rmi_dev);
|
||||
int error;
|
||||
|
||||
/* can happen if f30_data.disable is set */
|
||||
/* can happen if gpio_data.disable is set */
|
||||
if (!f30)
|
||||
return 0;
|
||||
|
||||
if (pdata->f30_data.trackstick_buttons) {
|
||||
if (pdata->gpio_data.trackstick_buttons) {
|
||||
/* Try [re-]establish link to F03. */
|
||||
f30->f03 = rmi_find_function(fn->rmi_dev, 0x03);
|
||||
f30->trackstick_buttons = f30->f03 != NULL;
|
||||
}
|
||||
|
||||
if (pdata->f30_data.disable) {
|
||||
if (pdata->gpio_data.disable) {
|
||||
drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask);
|
||||
} else {
|
||||
/* Write Control Register values back to device */
|
||||
|
@ -245,10 +245,10 @@ static int rmi_f30_map_gpios(struct rmi_function *fn,
|
|||
if (!rmi_f30_is_valid_button(i, f30->ctrl))
|
||||
continue;
|
||||
|
||||
if (pdata->f30_data.trackstick_buttons &&
|
||||
if (pdata->gpio_data.trackstick_buttons &&
|
||||
i >= TRACKSTICK_RANGE_START && i < TRACKSTICK_RANGE_END) {
|
||||
f30->gpioled_key_map[i] = trackstick_button++;
|
||||
} else if (!pdata->f30_data.buttonpad || !button_mapped) {
|
||||
} else if (!pdata->gpio_data.buttonpad || !button_mapped) {
|
||||
f30->gpioled_key_map[i] = button;
|
||||
input_set_capability(input, EV_KEY, button++);
|
||||
button_mapped = true;
|
||||
|
@ -264,7 +264,7 @@ static int rmi_f30_map_gpios(struct rmi_function *fn,
|
|||
* but I am not sure, so use only the pdata info and the number of
|
||||
* mapped buttons.
|
||||
*/
|
||||
if (pdata->f30_data.buttonpad || (button - BTN_LEFT == 1))
|
||||
if (pdata->gpio_data.buttonpad || (button - BTN_LEFT == 1))
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
|
||||
return 0;
|
||||
|
@ -372,7 +372,7 @@ static int rmi_f30_probe(struct rmi_function *fn)
|
|||
struct f30_data *f30;
|
||||
int error;
|
||||
|
||||
if (pdata->f30_data.disable)
|
||||
if (pdata->gpio_data.disable)
|
||||
return 0;
|
||||
|
||||
if (!drv_data->input) {
|
||||
|
|
|
@ -1364,9 +1364,14 @@ int rmi_f34v7_probe(struct f34_data *f34)
|
|||
f34->bl_version = 6;
|
||||
} else if (f34->bootloader_id[1] == 7) {
|
||||
f34->bl_version = 7;
|
||||
} else if (f34->bootloader_id[1] == 8) {
|
||||
f34->bl_version = 8;
|
||||
} else {
|
||||
dev_err(&f34->fn->dev, "%s: Unrecognized bootloader version\n",
|
||||
__func__);
|
||||
dev_err(&f34->fn->dev,
|
||||
"%s: Unrecognized bootloader version: %d (%c) %d (%c)\n",
|
||||
__func__,
|
||||
f34->bootloader_id[0], f34->bootloader_id[0],
|
||||
f34->bootloader_id[1], f34->bootloader_id[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020 Synaptics Incorporated
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include "rmi_driver.h"
|
||||
|
||||
#define RMI_F3A_MAX_GPIO_COUNT 128
|
||||
#define RMI_F3A_MAX_REG_SIZE DIV_ROUND_UP(RMI_F3A_MAX_GPIO_COUNT, 8)
|
||||
|
||||
/* Defs for Query 0 */
|
||||
#define RMI_F3A_GPIO_COUNT 0x7F
|
||||
|
||||
#define RMI_F3A_DATA_REGS_MAX_SIZE RMI_F3A_MAX_REG_SIZE
|
||||
|
||||
#define TRACKSTICK_RANGE_START 3
|
||||
#define TRACKSTICK_RANGE_END 6
|
||||
|
||||
struct f3a_data {
|
||||
/* Query Data */
|
||||
u8 gpio_count;
|
||||
|
||||
u8 register_count;
|
||||
|
||||
u8 data_regs[RMI_F3A_DATA_REGS_MAX_SIZE];
|
||||
u16 *gpio_key_map;
|
||||
|
||||
struct input_dev *input;
|
||||
|
||||
struct rmi_function *f03;
|
||||
bool trackstick_buttons;
|
||||
};
|
||||
|
||||
static void rmi_f3a_report_button(struct rmi_function *fn,
|
||||
struct f3a_data *f3a, unsigned int button)
|
||||
{
|
||||
u16 key_code = f3a->gpio_key_map[button];
|
||||
bool key_down = !(f3a->data_regs[0] & BIT(button));
|
||||
|
||||
if (f3a->trackstick_buttons &&
|
||||
button >= TRACKSTICK_RANGE_START &&
|
||||
button <= TRACKSTICK_RANGE_END) {
|
||||
rmi_f03_overwrite_button(f3a->f03, key_code, key_down);
|
||||
} else {
|
||||
rmi_dbg(RMI_DEBUG_FN, &fn->dev,
|
||||
"%s: call input report key (0x%04x) value (0x%02x)",
|
||||
__func__, key_code, key_down);
|
||||
input_report_key(f3a->input, key_code, key_down);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t rmi_f3a_attention(int irq, void *ctx)
|
||||
{
|
||||
struct rmi_function *fn = ctx;
|
||||
struct f3a_data *f3a = dev_get_drvdata(&fn->dev);
|
||||
struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev);
|
||||
int error;
|
||||
int i;
|
||||
|
||||
if (drvdata->attn_data.data) {
|
||||
if (drvdata->attn_data.size < f3a->register_count) {
|
||||
dev_warn(&fn->dev,
|
||||
"F3A interrupted, but data is missing\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
memcpy(f3a->data_regs, drvdata->attn_data.data,
|
||||
f3a->register_count);
|
||||
drvdata->attn_data.data += f3a->register_count;
|
||||
drvdata->attn_data.size -= f3a->register_count;
|
||||
} else {
|
||||
error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr,
|
||||
f3a->data_regs, f3a->register_count);
|
||||
if (error) {
|
||||
dev_err(&fn->dev,
|
||||
"%s: Failed to read F3a data registers: %d\n",
|
||||
__func__, error);
|
||||
return IRQ_RETVAL(error);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < f3a->gpio_count; i++)
|
||||
if (f3a->gpio_key_map[i] != KEY_RESERVED)
|
||||
rmi_f3a_report_button(fn, f3a, i);
|
||||
if (f3a->trackstick_buttons)
|
||||
rmi_f03_commit_buttons(f3a->f03);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rmi_f3a_config(struct rmi_function *fn)
|
||||
{
|
||||
struct f3a_data *f3a = dev_get_drvdata(&fn->dev);
|
||||
struct rmi_driver *drv = fn->rmi_dev->driver;
|
||||
const struct rmi_device_platform_data *pdata =
|
||||
rmi_get_platform_data(fn->rmi_dev);
|
||||
|
||||
if (!f3a)
|
||||
return 0;
|
||||
|
||||
if (pdata->gpio_data.trackstick_buttons) {
|
||||
/* Try [re-]establish link to F03. */
|
||||
f3a->f03 = rmi_find_function(fn->rmi_dev, 0x03);
|
||||
f3a->trackstick_buttons = f3a->f03 != NULL;
|
||||
}
|
||||
|
||||
drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool rmi_f3a_is_valid_button(int button, struct f3a_data *f3a,
|
||||
u8 *query1_regs, u8 *ctrl1_regs)
|
||||
{
|
||||
/* gpio exist && direction input */
|
||||
return (query1_regs[0] & BIT(button)) && !(ctrl1_regs[0] & BIT(button));
|
||||
}
|
||||
|
||||
static int rmi_f3a_map_gpios(struct rmi_function *fn, struct f3a_data *f3a,
|
||||
u8 *query1_regs, u8 *ctrl1_regs)
|
||||
{
|
||||
const struct rmi_device_platform_data *pdata =
|
||||
rmi_get_platform_data(fn->rmi_dev);
|
||||
struct input_dev *input = f3a->input;
|
||||
unsigned int button = BTN_LEFT;
|
||||
unsigned int trackstick_button = BTN_LEFT;
|
||||
bool button_mapped = false;
|
||||
int i;
|
||||
int button_count = min_t(u8, f3a->gpio_count, TRACKSTICK_RANGE_END);
|
||||
|
||||
f3a->gpio_key_map = devm_kcalloc(&fn->dev,
|
||||
button_count,
|
||||
sizeof(f3a->gpio_key_map[0]),
|
||||
GFP_KERNEL);
|
||||
if (!f3a->gpio_key_map) {
|
||||
dev_err(&fn->dev, "Failed to allocate gpio map memory.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < button_count; i++) {
|
||||
if (!rmi_f3a_is_valid_button(i, f3a, query1_regs, ctrl1_regs))
|
||||
continue;
|
||||
|
||||
if (pdata->gpio_data.trackstick_buttons &&
|
||||
i >= TRACKSTICK_RANGE_START &&
|
||||
i < TRACKSTICK_RANGE_END) {
|
||||
f3a->gpio_key_map[i] = trackstick_button++;
|
||||
} else if (!pdata->gpio_data.buttonpad || !button_mapped) {
|
||||
f3a->gpio_key_map[i] = button;
|
||||
input_set_capability(input, EV_KEY, button++);
|
||||
button_mapped = true;
|
||||
}
|
||||
}
|
||||
input->keycode = f3a->gpio_key_map;
|
||||
input->keycodesize = sizeof(f3a->gpio_key_map[0]);
|
||||
input->keycodemax = f3a->gpio_count;
|
||||
|
||||
if (pdata->gpio_data.buttonpad || (button - BTN_LEFT == 1))
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f3a_initialize(struct rmi_function *fn, struct f3a_data *f3a)
|
||||
{
|
||||
u8 query1[RMI_F3A_MAX_REG_SIZE];
|
||||
u8 ctrl1[RMI_F3A_MAX_REG_SIZE];
|
||||
u8 buf;
|
||||
int error;
|
||||
|
||||
error = rmi_read(fn->rmi_dev, fn->fd.query_base_addr, &buf);
|
||||
if (error < 0) {
|
||||
dev_err(&fn->dev, "Failed to read general info register: %d\n",
|
||||
error);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
f3a->gpio_count = buf & RMI_F3A_GPIO_COUNT;
|
||||
f3a->register_count = DIV_ROUND_UP(f3a->gpio_count, 8);
|
||||
|
||||
/* Query1 -> gpio exist */
|
||||
error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 1,
|
||||
query1, f3a->register_count);
|
||||
if (error) {
|
||||
dev_err(&fn->dev, "Failed to read query1 register\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Ctrl1 -> gpio direction */
|
||||
error = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr + 1,
|
||||
ctrl1, f3a->register_count);
|
||||
if (error) {
|
||||
dev_err(&fn->dev, "Failed to read control1 register\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = rmi_f3a_map_gpios(fn, f3a, query1, ctrl1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f3a_probe(struct rmi_function *fn)
|
||||
{
|
||||
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||
struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
|
||||
struct f3a_data *f3a;
|
||||
int error;
|
||||
|
||||
if (!drv_data->input) {
|
||||
dev_info(&fn->dev, "F3A: no input device found, ignoring\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
f3a = devm_kzalloc(&fn->dev, sizeof(*f3a), GFP_KERNEL);
|
||||
if (!f3a)
|
||||
return -ENOMEM;
|
||||
|
||||
f3a->input = drv_data->input;
|
||||
|
||||
error = rmi_f3a_initialize(fn, f3a);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
dev_set_drvdata(&fn->dev, f3a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rmi_function_handler rmi_f3a_handler = {
|
||||
.driver = {
|
||||
.name = "rmi4_f3a",
|
||||
},
|
||||
.func = 0x3a,
|
||||
.probe = rmi_f3a_probe,
|
||||
.config = rmi_f3a_config,
|
||||
.attention = rmi_f3a_attention,
|
||||
};
|
|
@ -211,7 +211,6 @@ static int sun4i_ps2_probe(struct platform_device *pdev)
|
|||
struct sun4i_ps2data *drvdata;
|
||||
struct serio *serio;
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned int irq;
|
||||
int error;
|
||||
|
||||
drvdata = kzalloc(sizeof(struct sun4i_ps2data), GFP_KERNEL);
|
||||
|
@ -264,14 +263,12 @@ static int sun4i_ps2_probe(struct platform_device *pdev)
|
|||
writel(0, drvdata->reg_base + PS2_REG_GCTL);
|
||||
|
||||
/* Get IRQ for the device */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!irq) {
|
||||
dev_err(dev, "no IRQ found\n");
|
||||
error = -ENXIO;
|
||||
drvdata->irq = platform_get_irq(pdev, 0);
|
||||
if (drvdata->irq < 0) {
|
||||
error = drvdata->irq;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
drvdata->irq = irq;
|
||||
drvdata->serio = serio;
|
||||
drvdata->dev = dev;
|
||||
|
||||
|
|
|
@ -1322,4 +1322,16 @@ config TOUCHSCREEN_IQS5XX
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called iqs5xx.
|
||||
|
||||
config TOUCHSCREEN_ZINITIX
|
||||
tristate "Zinitix touchscreen support"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a touchscreen using Zinitix bt541,
|
||||
or something similar enough.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called zinitix.
|
||||
|
||||
endif
|
||||
|
|
|
@ -111,3 +111,4 @@ obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
/* FW read command, 0x53 0x?? 0x0, 0x01 */
|
||||
#define E_ELAN_INFO_FW_VER 0x00
|
||||
#define E_ELAN_INFO_BC_VER 0x10
|
||||
#define E_ELAN_INFO_REK 0xE0
|
||||
#define E_ELAN_INFO_REK 0xD0
|
||||
#define E_ELAN_INFO_TEST_VER 0xE0
|
||||
#define E_ELAN_INFO_FW_ID 0xF0
|
||||
#define E_INFO_OSR 0xD6
|
||||
|
@ -134,6 +134,7 @@ struct elants_data {
|
|||
u8 bc_version;
|
||||
u8 iap_version;
|
||||
u16 hw_version;
|
||||
u8 major_res;
|
||||
unsigned int x_res; /* resolution in units/mm */
|
||||
unsigned int y_res;
|
||||
unsigned int x_max;
|
||||
|
@ -459,6 +460,9 @@ static int elants_i2c_query_ts_info(struct elants_data *ts)
|
|||
rows = resp[2] + resp[6] + resp[10];
|
||||
cols = resp[3] + resp[7] + resp[11];
|
||||
|
||||
/* Get report resolution value of ABS_MT_TOUCH_MAJOR */
|
||||
ts->major_res = resp[16];
|
||||
|
||||
/* Process mm_to_pixel information */
|
||||
error = elants_i2c_execute_command(client,
|
||||
get_osr_cmd, sizeof(get_osr_cmd),
|
||||
|
@ -1325,6 +1329,8 @@ static int elants_i2c_probe(struct i2c_client *client,
|
|||
0, MT_TOOL_PALM, 0, 0);
|
||||
input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
|
||||
input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
|
||||
if (ts->major_res > 0)
|
||||
input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, ts->major_res);
|
||||
|
||||
touchscreen_parse_properties(ts->input, true, &ts->prop);
|
||||
|
||||
|
|
|
@ -315,9 +315,8 @@ static irqreturn_t adc_irq_fn(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int imx6ul_tsc_open(struct input_dev *input_dev)
|
||||
static int imx6ul_tsc_start(struct imx6ul_tsc *tsc)
|
||||
{
|
||||
struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(tsc->adc_clk);
|
||||
|
@ -349,16 +348,29 @@ disable_adc_clk:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void imx6ul_tsc_close(struct input_dev *input_dev)
|
||||
static void imx6ul_tsc_stop(struct imx6ul_tsc *tsc)
|
||||
{
|
||||
struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
|
||||
|
||||
imx6ul_tsc_disable(tsc);
|
||||
|
||||
clk_disable_unprepare(tsc->tsc_clk);
|
||||
clk_disable_unprepare(tsc->adc_clk);
|
||||
}
|
||||
|
||||
|
||||
static int imx6ul_tsc_open(struct input_dev *input_dev)
|
||||
{
|
||||
struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
|
||||
|
||||
return imx6ul_tsc_start(tsc);
|
||||
}
|
||||
|
||||
static void imx6ul_tsc_close(struct input_dev *input_dev)
|
||||
{
|
||||
struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
|
||||
|
||||
imx6ul_tsc_stop(tsc);
|
||||
}
|
||||
|
||||
static int imx6ul_tsc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
@ -509,12 +521,8 @@ static int __maybe_unused imx6ul_tsc_suspend(struct device *dev)
|
|||
|
||||
mutex_lock(&input_dev->mutex);
|
||||
|
||||
if (input_dev->users) {
|
||||
imx6ul_tsc_disable(tsc);
|
||||
|
||||
clk_disable_unprepare(tsc->tsc_clk);
|
||||
clk_disable_unprepare(tsc->adc_clk);
|
||||
}
|
||||
if (input_dev->users)
|
||||
imx6ul_tsc_stop(tsc);
|
||||
|
||||
mutex_unlock(&input_dev->mutex);
|
||||
|
||||
|
@ -530,22 +538,11 @@ static int __maybe_unused imx6ul_tsc_resume(struct device *dev)
|
|||
|
||||
mutex_lock(&input_dev->mutex);
|
||||
|
||||
if (input_dev->users) {
|
||||
retval = clk_prepare_enable(tsc->adc_clk);
|
||||
if (retval)
|
||||
goto out;
|
||||
if (input_dev->users)
|
||||
retval = imx6ul_tsc_start(tsc);
|
||||
|
||||
retval = clk_prepare_enable(tsc->tsc_clk);
|
||||
if (retval) {
|
||||
clk_disable_unprepare(tsc->adc_clk);
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = imx6ul_tsc_init(tsc);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&input_dev->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
|
||||
/* Touch relative info */
|
||||
#define RM_MAX_RETRIES 3
|
||||
#define RM_RETRY_DELAY_MS 20
|
||||
#define RM_MAX_TOUCH_NUM 10
|
||||
#define RM_BOOT_DELAY_MS 100
|
||||
|
||||
|
@ -136,83 +137,82 @@ struct raydium_data {
|
|||
bool wake_irq_enabled;
|
||||
};
|
||||
|
||||
static int raydium_i2c_send(struct i2c_client *client,
|
||||
u8 addr, const void *data, size_t len)
|
||||
static int raydium_i2c_xfer(struct i2c_client *client,
|
||||
u32 addr, void *data, size_t len, bool is_read)
|
||||
{
|
||||
u8 *buf;
|
||||
int tries = 0;
|
||||
int ret;
|
||||
struct raydium_bank_switch_header {
|
||||
u8 cmd;
|
||||
__be32 be_addr;
|
||||
} __packed header = {
|
||||
.cmd = RM_CMD_BANK_SWITCH,
|
||||
.be_addr = cpu_to_be32(addr),
|
||||
};
|
||||
|
||||
buf = kmalloc(len + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
u8 reg_addr = addr & 0xff;
|
||||
|
||||
buf[0] = addr;
|
||||
memcpy(buf + 1, data, len);
|
||||
|
||||
do {
|
||||
ret = i2c_master_send(client, buf, len + 1);
|
||||
if (likely(ret == len + 1))
|
||||
break;
|
||||
|
||||
msleep(20);
|
||||
} while (++tries < RM_MAX_RETRIES);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
if (unlikely(ret != len + 1)) {
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raydium_i2c_read(struct i2c_client *client,
|
||||
u8 addr, void *data, size_t len)
|
||||
{
|
||||
struct i2c_msg xfer[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = &addr,
|
||||
.len = sizeof(header),
|
||||
.buf = (u8 *)&header,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = ®_addr,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = len,
|
||||
.buf = data,
|
||||
.flags = is_read ? I2C_M_RD : 0,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* If address is greater than 255, then RM_CMD_BANK_SWITCH needs to be
|
||||
* sent first. Else, skip the header i.e. xfer[0].
|
||||
*/
|
||||
int xfer_start_idx = (addr > 0xff) ? 0 : 1;
|
||||
size_t xfer_count = ARRAY_SIZE(xfer) - xfer_start_idx;
|
||||
int ret;
|
||||
|
||||
ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
|
||||
if (unlikely(ret != ARRAY_SIZE(xfer)))
|
||||
return ret < 0 ? ret : -EIO;
|
||||
ret = i2c_transfer(client->adapter, &xfer[xfer_start_idx], xfer_count);
|
||||
if (likely(ret == xfer_count))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
return ret < 0 ? ret : -EIO;
|
||||
}
|
||||
|
||||
static int raydium_i2c_read_message(struct i2c_client *client,
|
||||
u32 addr, void *data, size_t len)
|
||||
static int raydium_i2c_send(struct i2c_client *client,
|
||||
u32 addr, const void *data, size_t len)
|
||||
{
|
||||
int tries = 0;
|
||||
int error;
|
||||
|
||||
do {
|
||||
error = raydium_i2c_xfer(client, addr, (void *)data, len,
|
||||
false);
|
||||
if (likely(!error))
|
||||
return 0;
|
||||
|
||||
msleep(RM_RETRY_DELAY_MS);
|
||||
} while (++tries < RM_MAX_RETRIES);
|
||||
|
||||
dev_err(&client->dev, "%s failed: %d\n", __func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int raydium_i2c_read(struct i2c_client *client,
|
||||
u32 addr, void *data, size_t len)
|
||||
{
|
||||
__be32 be_addr;
|
||||
size_t xfer_len;
|
||||
int error;
|
||||
|
||||
while (len) {
|
||||
xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
|
||||
|
||||
be_addr = cpu_to_be32(addr);
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
|
||||
&be_addr, sizeof(be_addr));
|
||||
if (!error)
|
||||
error = raydium_i2c_read(client, addr & 0xff,
|
||||
data, xfer_len);
|
||||
if (error)
|
||||
error = raydium_i2c_xfer(client, addr, data, xfer_len, true);
|
||||
if (unlikely(error))
|
||||
return error;
|
||||
|
||||
len -= xfer_len;
|
||||
|
@ -223,27 +223,13 @@ static int raydium_i2c_read_message(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int raydium_i2c_send_message(struct i2c_client *client,
|
||||
u32 addr, const void *data, size_t len)
|
||||
{
|
||||
__be32 be_addr = cpu_to_be32(addr);
|
||||
int error;
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
|
||||
&be_addr, sizeof(be_addr));
|
||||
if (!error)
|
||||
error = raydium_i2c_send(client, addr & 0xff, data, len);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int raydium_i2c_sw_reset(struct i2c_client *client)
|
||||
{
|
||||
const u8 soft_rst_cmd = 0x01;
|
||||
int error;
|
||||
|
||||
error = raydium_i2c_send_message(client, RM_RESET_MSG_ADDR,
|
||||
&soft_rst_cmd, sizeof(soft_rst_cmd));
|
||||
error = raydium_i2c_send(client, RM_RESET_MSG_ADDR, &soft_rst_cmd,
|
||||
sizeof(soft_rst_cmd));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "software reset failed: %d\n", error);
|
||||
return error;
|
||||
|
@ -295,9 +281,8 @@ static int raydium_i2c_query_ts_info(struct raydium_data *ts)
|
|||
if (error)
|
||||
continue;
|
||||
|
||||
error = raydium_i2c_read_message(client,
|
||||
le32_to_cpu(query_bank_addr),
|
||||
&ts->info, sizeof(ts->info));
|
||||
error = raydium_i2c_read(client, le32_to_cpu(query_bank_addr),
|
||||
&ts->info, sizeof(ts->info));
|
||||
if (error)
|
||||
continue;
|
||||
|
||||
|
@ -834,8 +819,8 @@ static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
|
|||
if (ts->boot_mode != RAYDIUM_TS_MAIN)
|
||||
goto out;
|
||||
|
||||
error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
|
||||
ts->report_data, ts->pkg_size);
|
||||
error = raydium_i2c_read(ts->client, ts->data_bank_addr,
|
||||
ts->report_data, ts->pkg_size);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -479,7 +479,7 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev,
|
|||
|
||||
mutex_lock(&sdata->mutex);
|
||||
|
||||
if (value & sdata->hover_enabled)
|
||||
if (value && sdata->hover_enabled)
|
||||
goto out;
|
||||
|
||||
if (sdata->running)
|
||||
|
|
|
@ -0,0 +1,581 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Register Map */
|
||||
|
||||
#define BT541_SWRESET_CMD 0x0000
|
||||
#define BT541_WAKEUP_CMD 0x0001
|
||||
|
||||
#define BT541_IDLE_CMD 0x0004
|
||||
#define BT541_SLEEP_CMD 0x0005
|
||||
|
||||
#define BT541_CLEAR_INT_STATUS_CMD 0x0003
|
||||
#define BT541_CALIBRATE_CMD 0x0006
|
||||
#define BT541_SAVE_STATUS_CMD 0x0007
|
||||
#define BT541_SAVE_CALIBRATION_CMD 0x0008
|
||||
#define BT541_RECALL_FACTORY_CMD 0x000f
|
||||
|
||||
#define BT541_THRESHOLD 0x0020
|
||||
|
||||
#define BT541_LARGE_PALM_REJECT_AREA_TH 0x003F
|
||||
|
||||
#define BT541_DEBUG_REG 0x0115 /* 0~7 */
|
||||
|
||||
#define BT541_TOUCH_MODE 0x0010
|
||||
#define BT541_CHIP_REVISION 0x0011
|
||||
#define BT541_FIRMWARE_VERSION 0x0012
|
||||
|
||||
#define ZINITIX_USB_DETECT 0x116
|
||||
|
||||
#define BT541_MINOR_FW_VERSION 0x0121
|
||||
|
||||
#define BT541_VENDOR_ID 0x001C
|
||||
#define BT541_HW_ID 0x0014
|
||||
|
||||
#define BT541_DATA_VERSION_REG 0x0013
|
||||
#define BT541_SUPPORTED_FINGER_NUM 0x0015
|
||||
#define BT541_EEPROM_INFO 0x0018
|
||||
#define BT541_INITIAL_TOUCH_MODE 0x0019
|
||||
|
||||
#define BT541_TOTAL_NUMBER_OF_X 0x0060
|
||||
#define BT541_TOTAL_NUMBER_OF_Y 0x0061
|
||||
|
||||
#define BT541_DELAY_RAW_FOR_HOST 0x007f
|
||||
|
||||
#define BT541_BUTTON_SUPPORTED_NUM 0x00B0
|
||||
#define BT541_BUTTON_SENSITIVITY 0x00B2
|
||||
#define BT541_DUMMY_BUTTON_SENSITIVITY 0X00C8
|
||||
|
||||
#define BT541_X_RESOLUTION 0x00C0
|
||||
#define BT541_Y_RESOLUTION 0x00C1
|
||||
|
||||
#define BT541_POINT_STATUS_REG 0x0080
|
||||
#define BT541_ICON_STATUS_REG 0x00AA
|
||||
|
||||
#define BT541_POINT_COORD_REG (BT541_POINT_STATUS_REG + 2)
|
||||
|
||||
#define BT541_AFE_FREQUENCY 0x0100
|
||||
#define BT541_DND_N_COUNT 0x0122
|
||||
#define BT541_DND_U_COUNT 0x0135
|
||||
|
||||
#define BT541_RAWDATA_REG 0x0200
|
||||
|
||||
#define BT541_EEPROM_INFO_REG 0x0018
|
||||
|
||||
#define BT541_INT_ENABLE_FLAG 0x00f0
|
||||
#define BT541_PERIODICAL_INTERRUPT_INTERVAL 0x00f1
|
||||
|
||||
#define BT541_BTN_WIDTH 0x016d
|
||||
|
||||
#define BT541_CHECKSUM_RESULT 0x012c
|
||||
|
||||
#define BT541_INIT_FLASH 0x01d0
|
||||
#define BT541_WRITE_FLASH 0x01d1
|
||||
#define BT541_READ_FLASH 0x01d2
|
||||
|
||||
#define ZINITIX_INTERNAL_FLAG_02 0x011e
|
||||
#define ZINITIX_INTERNAL_FLAG_03 0x011f
|
||||
|
||||
#define ZINITIX_I2C_CHECKSUM_WCNT 0x016a
|
||||
#define ZINITIX_I2C_CHECKSUM_RESULT 0x016c
|
||||
|
||||
/* Interrupt & status register flags */
|
||||
|
||||
#define BIT_PT_CNT_CHANGE BIT(0)
|
||||
#define BIT_DOWN BIT(1)
|
||||
#define BIT_MOVE BIT(2)
|
||||
#define BIT_UP BIT(3)
|
||||
#define BIT_PALM BIT(4)
|
||||
#define BIT_PALM_REJECT BIT(5)
|
||||
#define BIT_RESERVED_0 BIT(6)
|
||||
#define BIT_RESERVED_1 BIT(7)
|
||||
#define BIT_WEIGHT_CHANGE BIT(8)
|
||||
#define BIT_PT_NO_CHANGE BIT(9)
|
||||
#define BIT_REJECT BIT(10)
|
||||
#define BIT_PT_EXIST BIT(11)
|
||||
#define BIT_RESERVED_2 BIT(12)
|
||||
#define BIT_ERROR BIT(13)
|
||||
#define BIT_DEBUG BIT(14)
|
||||
#define BIT_ICON_EVENT BIT(15)
|
||||
|
||||
#define SUB_BIT_EXIST BIT(0)
|
||||
#define SUB_BIT_DOWN BIT(1)
|
||||
#define SUB_BIT_MOVE BIT(2)
|
||||
#define SUB_BIT_UP BIT(3)
|
||||
#define SUB_BIT_UPDATE BIT(4)
|
||||
#define SUB_BIT_WAIT BIT(5)
|
||||
|
||||
#define DEFAULT_TOUCH_POINT_MODE 2
|
||||
#define MAX_SUPPORTED_FINGER_NUM 5
|
||||
|
||||
#define CHIP_ON_DELAY 15 // ms
|
||||
#define FIRMWARE_ON_DELAY 40 // ms
|
||||
|
||||
struct point_coord {
|
||||
__le16 x;
|
||||
__le16 y;
|
||||
u8 width;
|
||||
u8 sub_status;
|
||||
// currently unused, but needed as padding:
|
||||
u8 minor_width;
|
||||
u8 angle;
|
||||
};
|
||||
|
||||
struct touch_event {
|
||||
__le16 status;
|
||||
u8 finger_cnt;
|
||||
u8 time_stamp;
|
||||
struct point_coord point_coord[MAX_SUPPORTED_FINGER_NUM];
|
||||
};
|
||||
|
||||
struct bt541_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
struct touchscreen_properties prop;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
u32 zinitix_mode;
|
||||
};
|
||||
|
||||
static int zinitix_read_data(struct i2c_client *client,
|
||||
u16 reg, void *values, size_t length)
|
||||
{
|
||||
__le16 reg_le = cpu_to_le16(reg);
|
||||
int ret;
|
||||
|
||||
/* A single i2c_transfer() transaction does not work here. */
|
||||
ret = i2c_master_send(client, (u8 *)®_le, sizeof(reg_le));
|
||||
if (ret != sizeof(reg_le))
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
ret = i2c_master_recv(client, (u8 *)values, length);
|
||||
if (ret != length)
|
||||
return ret < 0 ? ret : -EIO; ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zinitix_write_u16(struct i2c_client *client, u16 reg, u16 value)
|
||||
{
|
||||
__le16 packet[2] = {cpu_to_le16(reg), cpu_to_le16(value)};
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, (u8 *)packet, sizeof(packet));
|
||||
if (ret != sizeof(packet))
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zinitix_write_cmd(struct i2c_client *client, u16 reg)
|
||||
{
|
||||
__le16 reg_le = cpu_to_le16(reg);
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, (u8 *)®_le, sizeof(reg_le));
|
||||
if (ret != sizeof(reg_le))
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
{
|
||||
struct i2c_client *client = bt541->client;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
error = zinitix_write_cmd(client, BT541_SWRESET_CMD);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to write reset command\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, 0x0);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to reset interrupt enable flag\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* initialize */
|
||||
error = zinitix_write_u16(client, BT541_X_RESOLUTION,
|
||||
bt541->prop.max_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, BT541_Y_RESOLUTION,
|
||||
bt541->prop.max_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, BT541_SUPPORTED_FINGER_NUM,
|
||||
MAX_SUPPORTED_FINGER_NUM);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, BT541_INITIAL_TOUCH_MODE,
|
||||
bt541->zinitix_mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, BT541_TOUCH_MODE,
|
||||
bt541->zinitix_mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG,
|
||||
BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE |
|
||||
BIT_UP);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* clear queue */
|
||||
for (i = 0; i < 10; i++) {
|
||||
zinitix_write_cmd(client, BT541_CLEAR_INT_STATUS_CMD);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zinitix_init_regulators(struct bt541_ts_data *bt541)
|
||||
{
|
||||
struct i2c_client *client = bt541->client;
|
||||
int error;
|
||||
|
||||
bt541->supplies[0].supply = "vdd";
|
||||
bt541->supplies[1].supply = "vddo";
|
||||
error = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(bt541->supplies),
|
||||
bt541->supplies);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Failed to get regulators: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zinitix_send_power_on_sequence(struct bt541_ts_data *bt541)
|
||||
{
|
||||
int error;
|
||||
struct i2c_client *client = bt541->client;
|
||||
|
||||
error = zinitix_write_u16(client, 0xc000, 0x0001);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to send power sequence(vendor cmd enable)\n");
|
||||
return error;
|
||||
}
|
||||
udelay(10);
|
||||
|
||||
error = zinitix_write_cmd(client, 0xc004);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to send power sequence (intn clear)\n");
|
||||
return error;
|
||||
}
|
||||
udelay(10);
|
||||
|
||||
error = zinitix_write_u16(client, 0xc002, 0x0001);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to send power sequence (nvm init)\n");
|
||||
return error;
|
||||
}
|
||||
mdelay(2);
|
||||
|
||||
error = zinitix_write_u16(client, 0xc001, 0x0001);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to send power sequence (program start)\n");
|
||||
return error;
|
||||
}
|
||||
msleep(FIRMWARE_ON_DELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zinitix_report_finger(struct bt541_ts_data *bt541, int slot,
|
||||
const struct point_coord *p)
|
||||
{
|
||||
input_mt_slot(bt541->input_dev, slot);
|
||||
input_mt_report_slot_state(bt541->input_dev, MT_TOOL_FINGER, true);
|
||||
touchscreen_report_pos(bt541->input_dev, &bt541->prop,
|
||||
le16_to_cpu(p->x), le16_to_cpu(p->y), true);
|
||||
input_report_abs(bt541->input_dev, ABS_MT_TOUCH_MAJOR, p->width);
|
||||
}
|
||||
|
||||
static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
|
||||
{
|
||||
struct bt541_ts_data *bt541 = bt541_handler;
|
||||
struct i2c_client *client = bt541->client;
|
||||
struct touch_event touch_event;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
memset(&touch_event, 0, sizeof(struct touch_event));
|
||||
|
||||
error = zinitix_read_data(bt541->client, BT541_POINT_STATUS_REG,
|
||||
&touch_event, sizeof(struct touch_event));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to read in touchpoint struct\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++)
|
||||
if (touch_event.point_coord[i].sub_status & SUB_BIT_EXIST)
|
||||
zinitix_report_finger(bt541, i,
|
||||
&touch_event.point_coord[i]);
|
||||
|
||||
input_mt_sync_frame(bt541->input_dev);
|
||||
input_sync(bt541->input_dev);
|
||||
|
||||
out:
|
||||
zinitix_write_cmd(bt541->client, BT541_CLEAR_INT_STATUS_CMD);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int zinitix_start(struct bt541_ts_data *bt541)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = regulator_bulk_enable(ARRAY_SIZE(bt541->supplies),
|
||||
bt541->supplies);
|
||||
if (error) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Failed to enable regulators: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
msleep(CHIP_ON_DELAY);
|
||||
|
||||
error = zinitix_send_power_on_sequence(bt541);
|
||||
if (error) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Error while sending power-on sequence: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = zinitix_init_touch(bt541);
|
||||
if (error) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Error while configuring touch IC\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
enable_irq(bt541->client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zinitix_stop(struct bt541_ts_data *bt541)
|
||||
{
|
||||
int error;
|
||||
|
||||
disable_irq(bt541->client->irq);
|
||||
|
||||
error = regulator_bulk_disable(ARRAY_SIZE(bt541->supplies),
|
||||
bt541->supplies);
|
||||
if (error) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Failed to disable regulators: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zinitix_input_open(struct input_dev *dev)
|
||||
{
|
||||
struct bt541_ts_data *bt541 = input_get_drvdata(dev);
|
||||
|
||||
return zinitix_start(bt541);
|
||||
}
|
||||
|
||||
static void zinitix_input_close(struct input_dev *dev)
|
||||
{
|
||||
struct bt541_ts_data *bt541 = input_get_drvdata(dev);
|
||||
|
||||
zinitix_stop(bt541);
|
||||
}
|
||||
|
||||
static int zinitix_init_input_dev(struct bt541_ts_data *bt541)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
input_dev = devm_input_allocate_device(&bt541->client->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Failed to allocate input device.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_set_drvdata(input_dev, bt541);
|
||||
bt541->input_dev = input_dev;
|
||||
|
||||
input_dev->name = "Zinitix Capacitive TouchScreen";
|
||||
input_dev->phys = "input/ts";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->open = zinitix_input_open;
|
||||
input_dev->close = zinitix_input_close;
|
||||
|
||||
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_WIDTH_MAJOR, 0, 255, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
|
||||
|
||||
touchscreen_parse_properties(input_dev, true, &bt541->prop);
|
||||
if (!bt541->prop.max_x || !bt541->prop.max_y) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Touchscreen-size-x and/or touchscreen-size-y not set in dts\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = input_mt_init_slots(input_dev, MAX_SUPPORTED_FINGER_NUM,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Failed to initialize MT slots: %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
dev_err(&bt541->client->dev,
|
||||
"Failed to register input device: %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zinitix_ts_probe(struct i2c_client *client)
|
||||
{
|
||||
struct bt541_ts_data *bt541;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to assert adapter's support for plain I2C.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
bt541 = devm_kzalloc(&client->dev, sizeof(*bt541), GFP_KERNEL);
|
||||
if (!bt541)
|
||||
return -ENOMEM;
|
||||
|
||||
bt541->client = client;
|
||||
i2c_set_clientdata(client, bt541);
|
||||
|
||||
error = zinitix_init_regulators(bt541);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to initialize regulators: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = zinitix_init_input_dev(bt541);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to initialize input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = device_property_read_u32(&client->dev, "zinitix,mode",
|
||||
&bt541->zinitix_mode);
|
||||
if (error < 0) {
|
||||
/* fall back to mode 2 */
|
||||
bt541->zinitix_mode = DEFAULT_TOUCH_POINT_MODE;
|
||||
}
|
||||
|
||||
if (bt541->zinitix_mode != 2) {
|
||||
/*
|
||||
* If there are devices that don't support mode 2, support
|
||||
* for other modes (0, 1) will be needed.
|
||||
*/
|
||||
dev_err(&client->dev,
|
||||
"Malformed zinitix,mode property, must be 2 (supplied: %d)\n",
|
||||
bt541->zinitix_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, zinitix_ts_irq_handler,
|
||||
IRQF_ONESHOT, client->name, bt541);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused zinitix_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct bt541_ts_data *bt541 = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&bt541->input_dev->mutex);
|
||||
|
||||
if (bt541->input_dev->users)
|
||||
zinitix_stop(bt541);
|
||||
|
||||
mutex_unlock(&bt541->input_dev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused zinitix_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct bt541_ts_data *bt541 = i2c_get_clientdata(client);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&bt541->input_dev->mutex);
|
||||
|
||||
if (bt541->input_dev->users)
|
||||
ret = zinitix_start(bt541);
|
||||
|
||||
mutex_unlock(&bt541->input_dev->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(zinitix_pm_ops, zinitix_suspend, zinitix_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id zinitix_of_match[] = {
|
||||
{ .compatible = "zinitix,bt541" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zinitix_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver zinitix_ts_driver = {
|
||||
.probe_new = zinitix_ts_probe,
|
||||
.driver = {
|
||||
.name = "Zinitix-TS",
|
||||
.pm = &zinitix_pm_ops,
|
||||
.of_match_table = of_match_ptr(zinitix_of_match),
|
||||
},
|
||||
};
|
||||
module_i2c_driver(zinitix_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");
|
||||
MODULE_DESCRIPTION("Zinitix touchscreen driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1913,6 +1913,10 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
|
|||
TP_ACPI_HOTKEYSCAN_CALCULATOR,
|
||||
TP_ACPI_HOTKEYSCAN_BLUETOOTH,
|
||||
TP_ACPI_HOTKEYSCAN_KEYBOARD,
|
||||
TP_ACPI_HOTKEYSCAN_FN_RIGHT_SHIFT, /* Used by "Lenovo Quick Clean" */
|
||||
TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER,
|
||||
TP_ACPI_HOTKEYSCAN_PICKUP_PHONE,
|
||||
TP_ACPI_HOTKEYSCAN_HANGUP_PHONE,
|
||||
|
||||
/* Hotkey keymap size */
|
||||
TPACPI_HOTKEY_MAP_LEN
|
||||
|
@ -3429,11 +3433,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
|
|||
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
|
||||
KEY_UNKNOWN,
|
||||
|
||||
KEY_BOOKMARKS, /* Favorite app, 0x311 */
|
||||
KEY_RESERVED, /* Clipping tool */
|
||||
KEY_CALC, /* Calculator (above numpad, P52) */
|
||||
KEY_BLUETOOTH, /* Bluetooth */
|
||||
KEY_KEYBOARD /* Keyboard, 0x315 */
|
||||
KEY_BOOKMARKS, /* Favorite app, 0x311 */
|
||||
KEY_SELECTIVE_SCREENSHOT, /* Clipping tool */
|
||||
KEY_CALC, /* Calculator (above numpad, P52) */
|
||||
KEY_BLUETOOTH, /* Bluetooth */
|
||||
KEY_KEYBOARD, /* Keyboard, 0x315 */
|
||||
KEY_FN_RIGHT_SHIFT, /* Fn + right Shift */
|
||||
KEY_NOTIFICATION_CENTER, /* Notification Center */
|
||||
KEY_PICKUP_PHONE, /* Answer incoming call */
|
||||
KEY_HANGUP_PHONE, /* Decline incoming call */
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -102,15 +102,16 @@ struct rmi_2d_sensor_platform_data {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct rmi_f30_data - overrides defaults for a single F30 GPIOs/LED chip.
|
||||
* struct rmi_gpio_data - overrides defaults for a single F30/F3A GPIOs/LED
|
||||
* chip.
|
||||
* @buttonpad - the touchpad is a buttonpad, so enable only the first actual
|
||||
* button that is found.
|
||||
* @trackstick_buttons - Set when the function 30 is handling the physical
|
||||
* @trackstick_buttons - Set when the function 30 or 3a is handling the physical
|
||||
* buttons of the trackstick (as a PS/2 passthrough device).
|
||||
* @disable - the touchpad incorrectly reports F30 and it should be ignored.
|
||||
* @disable - the touchpad incorrectly reports F30/F3A and it should be ignored.
|
||||
* This is a special case which is due to misconfigured firmware.
|
||||
*/
|
||||
struct rmi_f30_data {
|
||||
struct rmi_gpio_data {
|
||||
bool buttonpad;
|
||||
bool trackstick_buttons;
|
||||
bool disable;
|
||||
|
@ -218,7 +219,7 @@ struct rmi_device_platform_data {
|
|||
/* function handler pdata */
|
||||
struct rmi_2d_sensor_platform_data sensor_pdata;
|
||||
struct rmi_f01_power_management power_management;
|
||||
struct rmi_f30_data f30_data;
|
||||
struct rmi_gpio_data gpio_data;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -515,6 +515,9 @@
|
|||
#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */
|
||||
#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */
|
||||
#define KEY_IMAGES 0x1ba /* AL Image Browser */
|
||||
#define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */
|
||||
#define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */
|
||||
#define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */
|
||||
|
||||
#define KEY_DEL_EOL 0x1c0
|
||||
#define KEY_DEL_EOS 0x1c1
|
||||
|
@ -542,6 +545,7 @@
|
|||
#define KEY_FN_F 0x1e2
|
||||
#define KEY_FN_S 0x1e3
|
||||
#define KEY_FN_B 0x1e4
|
||||
#define KEY_FN_RIGHT_SHIFT 0x1e5
|
||||
|
||||
#define KEY_BRL_DOT1 0x1f1
|
||||
#define KEY_BRL_DOT2 0x1f2
|
||||
|
|
Загрузка…
Ссылка в новой задаче