Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: "Updates for the input subsystem. This contains the following new drivers promised in the last merge window: - driver for touchscreen controller found in Surface 3 - driver for Pegasus Notetaker tablet - driver for Atmel Captouch Buttons - driver for Raydium I2C touchscreen controllers - powerkey driver for HISI 65xx SoC plus a few fixes" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits) Input: tty/vt/keyboard - use memdup_user() Input: pegasus_notetaker - set device mode in reset_resume() if in use Input: pegasus_notetaker - cancel workqueue's work in suspend() Input: pegasus_notetaker - fix usb_autopm calls to be balanced Input: pegasus_notetaker - handle usb control msg errors Input: wacom_w8001 - handle errors from input_mt_init_slots() Input: wacom_w8001 - resolution wasn't set for ABS_MT_POSITION_X/Y Input: pixcir_ts - add support for axis inversion / swapping Input: icn8318 - use of_touchscreen helpers for inverting / swapping axes Input: edt-ft5x06 - add support for inverting / swapping axes Input: of_touchscreen - add support for inverted / swapped axes Input: synaptics-rmi4 - use the RMI_F11_REL_BYTES define in rmi_f11_rel_pos_report Input: synaptics-rmi4 - remove unneeded variable Input: synaptics-rmi4 - remove pointer to rmi_function in f12_data Input: synaptics-rmi4 - support regulator supplies Input: raydium_i2c_ts - check CRC of incoming packets Input: xen-kbdfront - prefer xenbus_write() over xenbus_printf() where possible Input: fix a double word "is is" in include/linux/input.h Input: add powerkey driver for HISI 65xx SoC Input: apanel - spelling mistake - "skiping" -> "skipping" ...
This commit is contained in:
commit
d85486d471
36
Documentation/devicetree/bindings/input/atmel,captouch.txt
Normal file
36
Documentation/devicetree/bindings/input/atmel,captouch.txt
Normal file
@ -0,0 +1,36 @@
|
||||
Device tree bindings for Atmel capacitive touch device, typically
|
||||
an Atmel touch sensor connected to AtmegaXX MCU running firmware
|
||||
based on Qtouch library.
|
||||
|
||||
The node for this device must be a child of a I2C controller node, as the
|
||||
device communicates via I2C.
|
||||
|
||||
Required properties:
|
||||
|
||||
compatible: Must be "atmel,captouch".
|
||||
reg: The I2C slave address of the device.
|
||||
interrupts: Property describing the interrupt line the device
|
||||
is connected to. The device only has one interrupt
|
||||
source.
|
||||
linux,keycodes: Specifies an array of numeric keycode values to
|
||||
be used for reporting button presses. The array can
|
||||
contain up to 8 entries.
|
||||
|
||||
Optional properties:
|
||||
|
||||
autorepeat: Enables the Linux input system's autorepeat
|
||||
feature on the input device.
|
||||
|
||||
Example:
|
||||
|
||||
atmel-captouch@51 {
|
||||
compatible = "atmel,captouch";
|
||||
reg = <0x51>;
|
||||
interrupt-parent = <&tlmm>;
|
||||
interrupts = <67 IRQ_TYPE_EDGE_FALLING>;
|
||||
linux,keycodes = <BTN_0>, <BTN_1>,
|
||||
<BTN_2>, <BTN_3>,
|
||||
<BTN_4>, <BTN_5>,
|
||||
<BTN_6>, <BTN_7>;
|
||||
autorepeat;
|
||||
};
|
20
Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
Normal file
20
Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
Normal file
@ -0,0 +1,20 @@
|
||||
Raydium I2C touchscreen
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "raydium,rm32380"
|
||||
- reg: The I2C address of the device
|
||||
- interrupt-parent: the phandle for the interrupt controller
|
||||
- interrupts: interrupt to which the chip is connected
|
||||
See ../interrupt-controller/interrupts.txt
|
||||
Optional properties:
|
||||
- avdd-supply: analog power supply needed to power device
|
||||
- vccio-supply: IO Power source
|
||||
- reset-gpios: reset gpio the chip is connected to.
|
||||
|
||||
Example:
|
||||
touchscreen@39 {
|
||||
compatible = "raydium,rm32380";
|
||||
reg = <0x39>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <0x0 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
@ -22,6 +22,15 @@ See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
- syna,reset-delay-ms: The number of milliseconds to wait after resetting the
|
||||
device.
|
||||
|
||||
- syna,startup-delay-ms: The number of milliseconds to wait after powering on
|
||||
the device.
|
||||
|
||||
- vdd-supply: VDD power supply.
|
||||
See ../regulator/regulator.txt
|
||||
|
||||
- vio-supply: VIO power supply
|
||||
See ../regulator/regulator.txt
|
||||
|
||||
Function Parameters:
|
||||
Parameters specific to RMI functions are contained in child nodes of the rmi device
|
||||
node. Documentation for the parameters of each function can be found in:
|
||||
|
@ -214,6 +214,7 @@ raidsonic RaidSonic Technology GmbH
|
||||
ralink Mediatek/Ralink Technology Corp.
|
||||
ramtron Ramtron International
|
||||
raspberrypi Raspberry Pi Foundation
|
||||
raydium Raydium Semiconductor Corp.
|
||||
realtek Realtek Semiconductor Corp.
|
||||
renesas Renesas Electronics Corporation
|
||||
richtek Richtek Technology Corporation
|
||||
|
@ -218,8 +218,23 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
|
||||
}
|
||||
|
||||
input_event(dev, EV_KEY, BTN_TOUCH, count > 0);
|
||||
if (use_count)
|
||||
|
||||
if (use_count) {
|
||||
if (count == 0 &&
|
||||
!test_bit(ABS_MT_DISTANCE, dev->absbit) &&
|
||||
test_bit(ABS_DISTANCE, dev->absbit) &&
|
||||
input_abs_get_val(dev, ABS_DISTANCE) != 0) {
|
||||
/*
|
||||
* Force reporting BTN_TOOL_FINGER for devices that
|
||||
* only report general hover (and not per-contact
|
||||
* distance) when contact is in proximity but not
|
||||
* on the surface.
|
||||
*/
|
||||
count = 1;
|
||||
}
|
||||
|
||||
input_mt_report_finger_count(dev, count);
|
||||
}
|
||||
|
||||
if (oldest) {
|
||||
int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
|
||||
|
@ -153,8 +153,6 @@ static void input_pass_values(struct input_dev *dev,
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
add_input_randomness(vals->type, vals->code, vals->value);
|
||||
|
||||
/* trigger auto repeat for key events */
|
||||
if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
@ -371,9 +369,10 @@ static int input_get_disposition(struct input_dev *dev,
|
||||
static void input_handle_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
int disposition;
|
||||
int disposition = input_get_disposition(dev, type, code, &value);
|
||||
|
||||
disposition = input_get_disposition(dev, type, code, &value);
|
||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
||||
add_input_randomness(type, code, value);
|
||||
|
||||
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
|
||||
dev->event(dev, type, code, value);
|
||||
|
@ -32,7 +32,7 @@
|
||||
#define TC3589x_PULL_DOWN_MASK 0x1
|
||||
#define TC3589x_PULL_UP_MASK 0x2
|
||||
#define TC3589x_PULLUP_ALL_MASK 0xAA
|
||||
#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2))
|
||||
#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2)
|
||||
|
||||
/* Bit masks for IOCFG register */
|
||||
#define IOCFG_BALLCFG 0x01
|
||||
|
@ -552,7 +552,7 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
|
||||
|
||||
if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
|
||||
dev_err(kbc->dev,
|
||||
"keypad rows/columns not porperly specified\n");
|
||||
"keypad rows/columns not properly specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,20 @@ config INPUT_ARIZONA_HAPTICS
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called arizona-haptics.
|
||||
|
||||
config INPUT_ATMEL_CAPTOUCH
|
||||
tristate "Atmel Capacitive Touch Button Driver"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if an Atmel Capacitive Touch Button device which
|
||||
implements "captouch" protocol is connected to I2C bus. Typically
|
||||
this device consists of Atmel Touch sensor controlled by AtMegaXX
|
||||
MCU running firmware based on Qtouch library.
|
||||
One should find "atmel,captouch" node in the board specific DTS.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called atmel_captouch.
|
||||
|
||||
config INPUT_BMA150
|
||||
tristate "BMA150/SMB380 acceleration sensor support"
|
||||
depends on I2C
|
||||
@ -796,4 +810,13 @@ config INPUT_DRV2667_HAPTICS
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called drv2667-haptics.
|
||||
|
||||
config INPUT_HISI_POWERKEY
|
||||
tristate "Hisilicon PMIC ONKEY support"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
help
|
||||
Say Y to enable support for PMIC ONKEY.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hisi_powerkey.
|
||||
|
||||
endif
|
||||
|
@ -17,6 +17,7 @@ obj-$(CONFIG_INPUT_APANEL) += apanel.o
|
||||
obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
|
||||
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
|
||||
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
|
||||
obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o
|
||||
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
|
||||
obj-$(CONFIG_INPUT_BMA150) += bma150.o
|
||||
obj-$(CONFIG_INPUT_CM109) += cm109.o
|
||||
@ -34,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
|
||||
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
|
||||
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
|
||||
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
|
||||
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
|
||||
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
|
||||
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
|
||||
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
|
||||
|
@ -297,7 +297,7 @@ static int __init apanel_init(void)
|
||||
|
||||
if (slave != i2c_addr) {
|
||||
pr_notice(APANEL ": only one SMBus slave "
|
||||
"address supported, skiping device...\n");
|
||||
"address supported, skipping device...\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
290
drivers/input/misc/atmel_captouch.c
Normal file
290
drivers/input/misc/atmel_captouch.c
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Atmel Atmegaxx Capacitive Touch Button Driver
|
||||
*
|
||||
* Copyright (C) 2016 Google, inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* It's irrelevant that the HW used to develop captouch driver is based
|
||||
* on Atmega88PA part and uses QtouchADC parts for sensing touch.
|
||||
* Calling this driver "captouch" is an arbitrary way to distinguish
|
||||
* the protocol this driver supported by other atmel/qtouch drivers.
|
||||
*
|
||||
* Captouch driver supports a newer/different version of the I2C
|
||||
* registers/commands than the qt1070.c driver.
|
||||
* Don't let the similarity of the general driver structure fool you.
|
||||
*
|
||||
* For raw i2c access from userspace, use i2cset/i2cget
|
||||
* to poke at /dev/i2c-N devices.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Maximum number of buttons supported */
|
||||
#define MAX_NUM_OF_BUTTONS 8
|
||||
|
||||
/* Registers */
|
||||
#define REG_KEY1_THRESHOLD 0x02
|
||||
#define REG_KEY2_THRESHOLD 0x03
|
||||
#define REG_KEY3_THRESHOLD 0x04
|
||||
#define REG_KEY4_THRESHOLD 0x05
|
||||
|
||||
#define REG_KEY1_REF_H 0x20
|
||||
#define REG_KEY1_REF_L 0x21
|
||||
#define REG_KEY2_REF_H 0x22
|
||||
#define REG_KEY2_REF_L 0x23
|
||||
#define REG_KEY3_REF_H 0x24
|
||||
#define REG_KEY3_REF_L 0x25
|
||||
#define REG_KEY4_REF_H 0x26
|
||||
#define REG_KEY4_REF_L 0x27
|
||||
|
||||
#define REG_KEY1_DLT_H 0x30
|
||||
#define REG_KEY1_DLT_L 0x31
|
||||
#define REG_KEY2_DLT_H 0x32
|
||||
#define REG_KEY2_DLT_L 0x33
|
||||
#define REG_KEY3_DLT_H 0x34
|
||||
#define REG_KEY3_DLT_L 0x35
|
||||
#define REG_KEY4_DLT_H 0x36
|
||||
#define REG_KEY4_DLT_L 0x37
|
||||
|
||||
#define REG_KEY_STATE 0x3C
|
||||
|
||||
/*
|
||||
* @i2c_client: I2C slave device client pointer
|
||||
* @input: Input device pointer
|
||||
* @num_btn: Number of buttons
|
||||
* @keycodes: map of button# to KeyCode
|
||||
* @prev_btn: Previous key state to detect button "press" or "release"
|
||||
* @xfer_buf: I2C transfer buffer
|
||||
*/
|
||||
struct atmel_captouch_device {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
u32 num_btn;
|
||||
u32 keycodes[MAX_NUM_OF_BUTTONS];
|
||||
u8 prev_btn;
|
||||
u8 xfer_buf[8] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/*
|
||||
* Read from I2C slave device
|
||||
* The protocol is that the client has to provide both the register address
|
||||
* and the length, and while reading back the device would prepend the data
|
||||
* with address and length for verification.
|
||||
*/
|
||||
static int atmel_read(struct atmel_captouch_device *capdev,
|
||||
u8 reg, u8 *data, size_t len)
|
||||
{
|
||||
struct i2c_client *client = capdev->client;
|
||||
struct device *dev = &client->dev;
|
||||
struct i2c_msg msg[2];
|
||||
int err;
|
||||
|
||||
if (len > sizeof(capdev->xfer_buf) - 2)
|
||||
return -EINVAL;
|
||||
|
||||
capdev->xfer_buf[0] = reg;
|
||||
capdev->xfer_buf[1] = len;
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].buf = capdev->xfer_buf;
|
||||
msg[0].len = 2;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
msg[1].buf = capdev->xfer_buf;
|
||||
msg[1].len = len + 2;
|
||||
|
||||
err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
|
||||
if (err != ARRAY_SIZE(msg))
|
||||
return err < 0 ? err : -EIO;
|
||||
|
||||
if (capdev->xfer_buf[0] != reg) {
|
||||
dev_err(dev,
|
||||
"I2C read error: register address does not match (%#02x vs %02x)\n",
|
||||
capdev->xfer_buf[0], reg);
|
||||
return -ECOMM;
|
||||
}
|
||||
|
||||
memcpy(data, &capdev->xfer_buf[2], len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle interrupt and report the key changes to the input system.
|
||||
* Multi-touch can be supported; however, it really depends on whether
|
||||
* the device can multi-touch.
|
||||
*/
|
||||
static irqreturn_t atmel_captouch_isr(int irq, void *data)
|
||||
{
|
||||
struct atmel_captouch_device *capdev = data;
|
||||
struct device *dev = &capdev->client->dev;
|
||||
int error;
|
||||
int i;
|
||||
u8 new_btn;
|
||||
u8 changed_btn;
|
||||
|
||||
error = atmel_read(capdev, REG_KEY_STATE, &new_btn, 1);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to read button state: %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: button state %#02x\n", __func__, new_btn);
|
||||
|
||||
changed_btn = new_btn ^ capdev->prev_btn;
|
||||
capdev->prev_btn = new_btn;
|
||||
|
||||
for (i = 0; i < capdev->num_btn; i++) {
|
||||
if (changed_btn & BIT(i))
|
||||
input_report_key(capdev->input,
|
||||
capdev->keycodes[i],
|
||||
new_btn & BIT(i));
|
||||
}
|
||||
|
||||
input_sync(capdev->input);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe function to setup the device, input system and interrupt
|
||||
*/
|
||||
static int atmel_captouch_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct atmel_captouch_device *capdev;
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *node;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)) {
|
||||
dev_err(dev, "needed i2c functionality is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
capdev = devm_kzalloc(dev, sizeof(*capdev), GFP_KERNEL);
|
||||
if (!capdev)
|
||||
return -ENOMEM;
|
||||
|
||||
capdev->client = client;
|
||||
i2c_set_clientdata(client, capdev);
|
||||
|
||||
err = atmel_read(capdev, REG_KEY_STATE,
|
||||
&capdev->prev_btn, sizeof(capdev->prev_btn));
|
||||
if (err) {
|
||||
dev_err(dev, "failed to read initial button state: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
capdev->input = devm_input_allocate_device(dev);
|
||||
if (!capdev->input) {
|
||||
dev_err(dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
capdev->input->id.bustype = BUS_I2C;
|
||||
capdev->input->id.product = 0x880A;
|
||||
capdev->input->id.version = 0;
|
||||
capdev->input->name = "ATMegaXX Capacitive Button Controller";
|
||||
__set_bit(EV_KEY, capdev->input->evbit);
|
||||
|
||||
node = dev->of_node;
|
||||
if (!node) {
|
||||
dev_err(dev, "failed to find matching node in device tree\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(node, "autorepeat"))
|
||||
__set_bit(EV_REP, capdev->input->evbit);
|
||||
|
||||
capdev->num_btn = of_property_count_u32_elems(node, "linux,keymap");
|
||||
if (capdev->num_btn > MAX_NUM_OF_BUTTONS)
|
||||
capdev->num_btn = MAX_NUM_OF_BUTTONS;
|
||||
|
||||
err = of_property_read_u32_array(node, "linux,keycodes",
|
||||
capdev->keycodes,
|
||||
capdev->num_btn);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"failed to read linux,keycode property: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < capdev->num_btn; i++)
|
||||
__set_bit(capdev->keycodes[i], capdev->input->keybit);
|
||||
|
||||
capdev->input->keycode = capdev->keycodes;
|
||||
capdev->input->keycodesize = sizeof(capdev->keycodes[0]);
|
||||
capdev->input->keycodemax = capdev->num_btn;
|
||||
|
||||
err = input_register_device(capdev->input);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devm_request_threaded_irq(dev, client->irq,
|
||||
NULL, atmel_captouch_isr,
|
||||
IRQF_ONESHOT,
|
||||
"atmel_captouch", capdev);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to request irq %d: %d\n",
|
||||
client->irq, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id atmel_captouch_of_id[] = {
|
||||
{
|
||||
.compatible = "atmel,captouch",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atmel_captouch_of_id);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id atmel_captouch_id[] = {
|
||||
{ "atmel_captouch", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atmel_captouch_id);
|
||||
|
||||
static struct i2c_driver atmel_captouch_driver = {
|
||||
.probe = atmel_captouch_probe,
|
||||
.id_table = atmel_captouch_id,
|
||||
.driver = {
|
||||
.name = "atmel_captouch",
|
||||
.of_match_table = of_match_ptr(atmel_captouch_of_id),
|
||||
},
|
||||
};
|
||||
module_i2c_driver(atmel_captouch_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Hung-yu Wu <hywu@google.com>");
|
||||
MODULE_DESCRIPTION("Atmel ATmegaXX Capacitance Touch Sensor I2C Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
142
drivers/input/misc/hisi_powerkey.c
Normal file
142
drivers/input/misc/hisi_powerkey.c
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Hisilicon PMIC powerkey driver
|
||||
*
|
||||
* Copyright (C) 2013 Hisilicon Ltd.
|
||||
* Copyright (C) 2015, 2016 Linaro Ltd.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* the held interrupt will trigger after 4 seconds */
|
||||
#define MAX_HELD_TIME (4 * MSEC_PER_SEC)
|
||||
|
||||
static irqreturn_t hi65xx_power_press_isr(int irq, void *q)
|
||||
{
|
||||
struct input_dev *input = q;
|
||||
|
||||
pm_wakeup_event(input->dev.parent, MAX_HELD_TIME);
|
||||
input_report_key(input, KEY_POWER, 1);
|
||||
input_sync(input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t hi65xx_power_release_isr(int irq, void *q)
|
||||
{
|
||||
struct input_dev *input = q;
|
||||
|
||||
pm_wakeup_event(input->dev.parent, MAX_HELD_TIME);
|
||||
input_report_key(input, KEY_POWER, 0);
|
||||
input_sync(input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t hi65xx_restart_toggle_isr(int irq, void *q)
|
||||
{
|
||||
struct input_dev *input = q;
|
||||
int value = test_bit(KEY_RESTART, input->key);
|
||||
|
||||
pm_wakeup_event(input->dev.parent, MAX_HELD_TIME);
|
||||
input_report_key(input, KEY_RESTART, !value);
|
||||
input_sync(input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
irqreturn_t (*handler)(int irq, void *q);
|
||||
} hi65xx_irq_info[] = {
|
||||
{ "down", hi65xx_power_press_isr },
|
||||
{ "up", hi65xx_power_release_isr },
|
||||
{ "hold 4s", hi65xx_restart_toggle_isr },
|
||||
};
|
||||
|
||||
static int hi65xx_powerkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct input_dev *input;
|
||||
int irq, i, error;
|
||||
|
||||
input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input->phys = "hisi_on/input0";
|
||||
input->name = "HISI 65xx PowerOn Key";
|
||||
|
||||
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||
input_set_capability(input, EV_KEY, KEY_RESTART);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) {
|
||||
|
||||
irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name);
|
||||
if (irq < 0) {
|
||||
error = irq;
|
||||
dev_err(dev, "couldn't get irq %s: %d\n",
|
||||
hi65xx_irq_info[i].name, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_any_context_irq(dev, irq,
|
||||
hi65xx_irq_info[i].handler,
|
||||
IRQF_ONESHOT,
|
||||
hi65xx_irq_info[i].name,
|
||||
input);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "couldn't request irq %s: %d\n",
|
||||
hi65xx_irq_info[i].name, error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register input device: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi65xx_powerkey_remove(struct platform_device *pdev)
|
||||
{
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver hi65xx_powerkey_driver = {
|
||||
.driver = {
|
||||
.name = "hi65xx-powerkey",
|
||||
},
|
||||
.probe = hi65xx_powerkey_probe,
|
||||
.remove = hi65xx_powerkey_remove,
|
||||
};
|
||||
module_platform_driver(hi65xx_powerkey_driver);
|
||||
|
||||
MODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com");
|
||||
MODULE_DESCRIPTION("Hisi PMIC Power key driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -124,7 +124,7 @@ regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic)
|
||||
|
||||
node = dev->of_node;
|
||||
if(!node) {
|
||||
dev_err(dev, "Missing dveice tree data\n");
|
||||
dev_err(dev, "Missing device tree data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -130,8 +130,8 @@ static int xenkbd_probe(struct xenbus_device *dev,
|
||||
if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
|
||||
abs = 0;
|
||||
if (abs) {
|
||||
ret = xenbus_printf(XBT_NIL, dev->nodename,
|
||||
"request-abs-pointer", "1");
|
||||
ret = xenbus_write(XBT_NIL, dev->nodename,
|
||||
"request-abs-pointer", "1");
|
||||
if (ret) {
|
||||
pr_warning("xenkbd: can't request abs-pointer");
|
||||
abs = 0;
|
||||
@ -327,8 +327,8 @@ InitWait:
|
||||
if (ret < 0)
|
||||
val = 0;
|
||||
if (val) {
|
||||
ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
|
||||
"request-abs-pointer", "1");
|
||||
ret = xenbus_write(XBT_NIL, info->xbdev->nodename,
|
||||
"request-abs-pointer", "1");
|
||||
if (ret)
|
||||
pr_warning("xenkbd: can't request abs-pointer");
|
||||
}
|
||||
|
@ -1708,7 +1708,7 @@ int elantech_init(struct psmouse *psmouse)
|
||||
snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1",
|
||||
psmouse->ps2dev.serio->phys);
|
||||
tp_dev->phys = etd->tp_phys;
|
||||
tp_dev->name = "Elantech PS/2 TrackPoint";
|
||||
tp_dev->name = "ETPS/2 Elantech TrackPoint";
|
||||
tp_dev->id.bustype = BUS_I8042;
|
||||
tp_dev->id.vendor = 0x0002;
|
||||
tp_dev->id.product = PSMOUSE_ELANTECH;
|
||||
|
@ -287,7 +287,7 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
|
||||
"%s/input1", psmouse->ps2dev.serio->phys);
|
||||
|
||||
dev2->phys = priv->phys;
|
||||
dev2->name = "PS/2 Touchpad";
|
||||
dev2->name = "LBPS/2 Fujitsu Lifebook Touchpad";
|
||||
dev2->id.bustype = BUS_I8042;
|
||||
dev2->id.vendor = 0x0002;
|
||||
dev2->id.product = PSMOUSE_LIFEBOOK;
|
||||
|
@ -81,26 +81,26 @@ struct f01_basic_properties {
|
||||
* This bit disables whatever sleep mode may be selected by the sleep_mode
|
||||
* field and forces the device to run at full power without sleeping.
|
||||
*/
|
||||
#define RMI_F01_CRTL0_NOSLEEP_BIT BIT(2)
|
||||
#define RMI_F01_CTRL0_NOSLEEP_BIT BIT(2)
|
||||
|
||||
/*
|
||||
* When this bit is set, the touch controller employs a noise-filtering
|
||||
* algorithm designed for use with a connected battery charger.
|
||||
*/
|
||||
#define RMI_F01_CRTL0_CHARGER_BIT BIT(5)
|
||||
#define RMI_F01_CTRL0_CHARGER_BIT BIT(5)
|
||||
|
||||
/*
|
||||
* Sets the report rate for the device. The effect of this setting is
|
||||
* highly product dependent. Check the spec sheet for your particular
|
||||
* touch sensor.
|
||||
*/
|
||||
#define RMI_F01_CRTL0_REPORTRATE_BIT BIT(6)
|
||||
#define RMI_F01_CTRL0_REPORTRATE_BIT BIT(6)
|
||||
|
||||
/*
|
||||
* Written by the host as an indicator that the device has been
|
||||
* successfully configured.
|
||||
*/
|
||||
#define RMI_F01_CRTL0_CONFIGURED_BIT BIT(7)
|
||||
#define RMI_F01_CTRL0_CONFIGURED_BIT BIT(7)
|
||||
|
||||
/**
|
||||
* @ctrl0 - see the bit definitions above.
|
||||
@ -330,10 +330,10 @@ static int rmi_f01_probe(struct rmi_function *fn)
|
||||
case RMI_F01_NOSLEEP_DEFAULT:
|
||||
break;
|
||||
case RMI_F01_NOSLEEP_OFF:
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_NOSLEEP_BIT;
|
||||
break;
|
||||
case RMI_F01_NOSLEEP_ON:
|
||||
f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -349,7 +349,7 @@ static int rmi_f01_probe(struct rmi_function *fn)
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||
}
|
||||
|
||||
f01->device_control.ctrl0 |= RMI_F01_CRTL0_CONFIGURED_BIT;
|
||||
f01->device_control.ctrl0 |= RMI_F01_CTRL0_CONFIGURED_BIT;
|
||||
|
||||
error = rmi_write(rmi_dev, fn->fd.control_base_addr,
|
||||
f01->device_control.ctrl0);
|
||||
@ -535,8 +535,8 @@ static int rmi_f01_suspend(struct rmi_function *fn)
|
||||
int error;
|
||||
|
||||
f01->old_nosleep =
|
||||
f01->device_control.ctrl0 & RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 & RMI_F01_CTRL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_NOSLEEP_BIT;
|
||||
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||
if (device_may_wakeup(fn->rmi_dev->xport->dev))
|
||||
@ -549,7 +549,7 @@ static int rmi_f01_suspend(struct rmi_function *fn)
|
||||
if (error) {
|
||||
dev_err(&fn->dev, "Failed to write sleep mode: %d.\n", error);
|
||||
if (f01->old_nosleep)
|
||||
f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||
f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
|
||||
return error;
|
||||
@ -564,7 +564,7 @@ static int rmi_f01_resume(struct rmi_function *fn)
|
||||
int error;
|
||||
|
||||
if (f01->old_nosleep)
|
||||
f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||
f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT;
|
||||
|
||||
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||
f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
|
||||
|
@ -530,8 +530,8 @@ static void rmi_f11_rel_pos_report(struct f11_data *f11, u8 n_finger)
|
||||
struct f11_2d_data *data = &f11->data;
|
||||
s8 x, y;
|
||||
|
||||
x = data->rel_pos[n_finger * 2];
|
||||
y = data->rel_pos[n_finger * 2 + 1];
|
||||
x = data->rel_pos[n_finger * RMI_F11_REL_BYTES];
|
||||
y = data->rel_pos[n_finger * RMI_F11_REL_BYTES + 1];
|
||||
|
||||
rmi_2d_sensor_rel_report(sensor, x, y);
|
||||
}
|
||||
@ -1241,7 +1241,6 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
|
||||
struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
|
||||
struct f11_data *f11 = dev_get_drvdata(&fn->dev);
|
||||
u16 data_base_addr = fn->fd.data_base_addr;
|
||||
u16 data_base_addr_offset = 0;
|
||||
int error;
|
||||
|
||||
if (rmi_dev->xport->attn_data) {
|
||||
@ -1251,8 +1250,7 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
|
||||
rmi_dev->xport->attn_size -= f11->sensor.attn_size;
|
||||
} else {
|
||||
error = rmi_read_block(rmi_dev,
|
||||
data_base_addr + data_base_addr_offset,
|
||||
f11->sensor.data_pkt,
|
||||
data_base_addr, f11->sensor.data_pkt,
|
||||
f11->sensor.pkt_size);
|
||||
if (error < 0)
|
||||
return error;
|
||||
@ -1260,7 +1258,6 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
|
||||
|
||||
rmi_f11_finger_handler(f11, &f11->sensor, irq_bits,
|
||||
drvdata->num_of_irq_regs);
|
||||
data_base_addr_offset += f11->sensor.pkt_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ enum rmi_f12_object_type {
|
||||
};
|
||||
|
||||
struct f12_data {
|
||||
struct rmi_function *fn;
|
||||
struct rmi_2d_sensor sensor;
|
||||
struct rmi_2d_sensor_platform_data sensor_pdata;
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include "rmi_driver.h"
|
||||
|
||||
#define BUFFER_SIZE_INCREMENT 32
|
||||
@ -37,6 +39,9 @@ struct rmi_i2c_xport {
|
||||
|
||||
u8 *tx_buf;
|
||||
size_t tx_buf_size;
|
||||
|
||||
struct regulator_bulk_data supplies[2];
|
||||
u32 startup_delay;
|
||||
};
|
||||
|
||||
#define RMI_PAGE_SELECT_REGISTER 0xff
|
||||
@ -246,6 +251,24 @@ static int rmi_i2c_probe(struct i2c_client *client,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rmi_i2c->supplies[0].supply = "vdd";
|
||||
rmi_i2c->supplies[1].supply = "vio";
|
||||
retval = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(rmi_i2c->supplies),
|
||||
rmi_i2c->supplies);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
retval = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
|
||||
rmi_i2c->supplies);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms",
|
||||
&rmi_i2c->startup_delay);
|
||||
|
||||
msleep(rmi_i2c->startup_delay);
|
||||
|
||||
rmi_i2c->client = client;
|
||||
mutex_init(&rmi_i2c->page_mutex);
|
||||
|
||||
@ -286,6 +309,8 @@ static int rmi_i2c_remove(struct i2c_client *client)
|
||||
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||
|
||||
rmi_unregister_transport_device(&rmi_i2c->xport);
|
||||
regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
|
||||
rmi_i2c->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -308,6 +333,10 @@ static int rmi_i2c_suspend(struct device *dev)
|
||||
dev_warn(dev, "Failed to enable irq for wake: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
|
||||
rmi_i2c->supplies);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -317,6 +346,13 @@ static int rmi_i2c_resume(struct device *dev)
|
||||
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
|
||||
rmi_i2c->supplies);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msleep(rmi_i2c->startup_delay);
|
||||
|
||||
enable_irq(rmi_i2c->irq);
|
||||
if (device_may_wakeup(&client->dev)) {
|
||||
ret = disable_irq_wake(rmi_i2c->irq);
|
||||
@ -346,6 +382,9 @@ static int rmi_i2c_runtime_suspend(struct device *dev)
|
||||
|
||||
disable_irq(rmi_i2c->irq);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
|
||||
rmi_i2c->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -355,6 +394,13 @@ static int rmi_i2c_runtime_resume(struct device *dev)
|
||||
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
|
||||
rmi_i2c->supplies);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msleep(rmi_i2c->startup_delay);
|
||||
|
||||
enable_irq(rmi_i2c->irq);
|
||||
|
||||
ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev);
|
||||
|
@ -56,7 +56,7 @@ static int check_data(int data)
|
||||
/* it should be odd */
|
||||
if (!(parity & 0x01)) {
|
||||
dev_warn(&ams_delta_serio->dev,
|
||||
"paritiy check failed, data=0x%X parity=0x%X\n",
|
||||
"parity check failed, data=0x%X parity=0x%X\n",
|
||||
data, parity);
|
||||
return SERIO_PARITY;
|
||||
}
|
||||
|
@ -73,6 +73,21 @@ config TABLET_USB_KBTAB
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called kbtab.
|
||||
|
||||
config TABLET_USB_PEGASUS
|
||||
tristate "Pegasus Mobile Notetaker Pen input tablet support"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select USB
|
||||
help
|
||||
Say Y here if you want to use the Pegasus Mobile Notetaker,
|
||||
also known as:
|
||||
Genie e-note The Notetaker,
|
||||
Staedtler Digital ballpoint pen 990 01,
|
||||
IRISnotes Express or
|
||||
NEWLink Digital Note Taker.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pegasus_notetaker.
|
||||
|
||||
config TABLET_SERIAL_WACOM4
|
||||
tristate "Wacom protocol 4 serial tablet support"
|
||||
select SERIO
|
||||
|
@ -8,4 +8,5 @@ obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
|
||||
obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o
|
||||
obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
|
||||
obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_TABLET_USB_PEGASUS) += pegasus_notetaker.o
|
||||
obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o
|
||||
|
450
drivers/input/tablet/pegasus_notetaker.c
Normal file
450
drivers/input/tablet/pegasus_notetaker.c
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Pegasus Mobile Notetaker Pen input tablet driver
|
||||
*
|
||||
* Copyright (c) 2016 Martin Kepplinger <martink@posteo.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* request packet (control endpoint):
|
||||
* |-------------------------------------|
|
||||
* | Report ID | Nr of bytes | command |
|
||||
* | (1 byte) | (1 byte) | (n bytes) |
|
||||
* |-------------------------------------|
|
||||
* | 0x02 | n | |
|
||||
* |-------------------------------------|
|
||||
*
|
||||
* data packet after set xy mode command, 0x80 0xb5 0x02 0x01
|
||||
* and pen is in range:
|
||||
*
|
||||
* byte byte name value (bits)
|
||||
* --------------------------------------------
|
||||
* 0 status 0 1 0 0 0 0 X X
|
||||
* 1 color 0 0 0 0 H 0 S T
|
||||
* 2 X low
|
||||
* 3 X high
|
||||
* 4 Y low
|
||||
* 5 Y high
|
||||
*
|
||||
* X X battery state:
|
||||
* no state reported 0x00
|
||||
* battery low 0x01
|
||||
* battery good 0x02
|
||||
*
|
||||
* H Hovering
|
||||
* S Switch 1 (pen button)
|
||||
* T Tip
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* USB HID defines */
|
||||
#define USB_REQ_GET_REPORT 0x01
|
||||
#define USB_REQ_SET_REPORT 0x09
|
||||
|
||||
#define USB_VENDOR_ID_PEGASUSTECH 0x0e20
|
||||
#define USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100 0x0101
|
||||
|
||||
/* device specific defines */
|
||||
#define NOTETAKER_REPORT_ID 0x02
|
||||
#define NOTETAKER_SET_CMD 0x80
|
||||
#define NOTETAKER_SET_MODE 0xb5
|
||||
|
||||
#define NOTETAKER_LED_MOUSE 0x02
|
||||
#define PEN_MODE_XY 0x01
|
||||
|
||||
#define SPECIAL_COMMAND 0x80
|
||||
#define BUTTON_PRESSED 0xb5
|
||||
#define COMMAND_VERSION 0xa9
|
||||
|
||||
/* in xy data packet */
|
||||
#define BATTERY_NO_REPORT 0x40
|
||||
#define BATTERY_LOW 0x41
|
||||
#define BATTERY_GOOD 0x42
|
||||
#define PEN_BUTTON_PRESSED BIT(1)
|
||||
#define PEN_TIP BIT(0)
|
||||
|
||||
struct pegasus {
|
||||
unsigned char *data;
|
||||
u8 data_len;
|
||||
dma_addr_t data_dma;
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
struct usb_interface *intf;
|
||||
struct urb *irq;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct work_struct init;
|
||||
};
|
||||
|
||||
static int pegasus_control_msg(struct pegasus *pegasus, u8 *data, int len)
|
||||
{
|
||||
const int sizeof_buf = len + 2;
|
||||
int result;
|
||||
int error;
|
||||
u8 *cmd_buf;
|
||||
|
||||
cmd_buf = kmalloc(sizeof_buf, GFP_KERNEL);
|
||||
if (!cmd_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd_buf[0] = NOTETAKER_REPORT_ID;
|
||||
cmd_buf[1] = len;
|
||||
memcpy(cmd_buf + 2, data, len);
|
||||
|
||||
result = usb_control_msg(pegasus->usbdev,
|
||||
usb_sndctrlpipe(pegasus->usbdev, 0),
|
||||
USB_REQ_SET_REPORT,
|
||||
USB_TYPE_VENDOR | USB_DIR_OUT,
|
||||
0, 0, cmd_buf, sizeof_buf,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
kfree(cmd_buf);
|
||||
|
||||
if (unlikely(result != sizeof_buf)) {
|
||||
error = result < 0 ? result : -EIO;
|
||||
dev_err(&pegasus->usbdev->dev, "control msg error: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pegasus_set_mode(struct pegasus *pegasus, u8 mode, u8 led)
|
||||
{
|
||||
u8 cmd[] = { NOTETAKER_SET_CMD, NOTETAKER_SET_MODE, led, mode };
|
||||
|
||||
return pegasus_control_msg(pegasus, cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void pegasus_parse_packet(struct pegasus *pegasus)
|
||||
{
|
||||
unsigned char *data = pegasus->data;
|
||||
struct input_dev *dev = pegasus->dev;
|
||||
u16 x, y;
|
||||
|
||||
switch (data[0]) {
|
||||
case SPECIAL_COMMAND:
|
||||
/* device button pressed */
|
||||
if (data[1] == BUTTON_PRESSED)
|
||||
schedule_work(&pegasus->init);
|
||||
|
||||
break;
|
||||
|
||||
/* xy data */
|
||||
case BATTERY_LOW:
|
||||
dev_warn_once(&dev->dev, "Pen battery low\n");
|
||||
/* fall through */
|
||||
|
||||
case BATTERY_NO_REPORT:
|
||||
case BATTERY_GOOD:
|
||||
x = le16_to_cpup((__le16 *)&data[2]);
|
||||
y = le16_to_cpup((__le16 *)&data[4]);
|
||||
|
||||
/* pen-up event */
|
||||
if (x == 0 && y == 0)
|
||||
break;
|
||||
|
||||
input_report_key(dev, BTN_TOUCH, data[1] & PEN_TIP);
|
||||
input_report_key(dev, BTN_RIGHT, data[1] & PEN_BUTTON_PRESSED);
|
||||
input_report_key(dev, BTN_TOOL_PEN, 1);
|
||||
input_report_abs(dev, ABS_X, (s16)x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
|
||||
input_sync(dev);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn_once(&pegasus->usbdev->dev,
|
||||
"unknown answer from device\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pegasus_irq(struct urb *urb)
|
||||
{
|
||||
struct pegasus *pegasus = urb->context;
|
||||
struct usb_device *dev = pegasus->usbdev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
pegasus_parse_packet(pegasus);
|
||||
usb_mark_last_busy(pegasus->usbdev);
|
||||
break;
|
||||
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
dev_err(&dev->dev, "%s - urb shutting down with status: %d",
|
||||
__func__, urb->status);
|
||||
return;
|
||||
|
||||
default:
|
||||
dev_err(&dev->dev, "%s - nonzero urb status received: %d",
|
||||
__func__, urb->status);
|
||||
break;
|
||||
}
|
||||
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
|
||||
__func__, retval);
|
||||
}
|
||||
|
||||
static void pegasus_init(struct work_struct *work)
|
||||
{
|
||||
struct pegasus *pegasus = container_of(work, struct pegasus, init);
|
||||
int error;
|
||||
|
||||
error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE);
|
||||
if (error)
|
||||
dev_err(&pegasus->usbdev->dev, "pegasus_set_mode error: %d\n",
|
||||
error);
|
||||
}
|
||||
|
||||
static int pegasus_open(struct input_dev *dev)
|
||||
{
|
||||
struct pegasus *pegasus = input_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
error = usb_autopm_get_interface(pegasus->intf);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
pegasus->irq->dev = pegasus->usbdev;
|
||||
if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) {
|
||||
error = -EIO;
|
||||
goto err_autopm_put;
|
||||
}
|
||||
|
||||
error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE);
|
||||
if (error)
|
||||
goto err_kill_urb;
|
||||
|
||||
return 0;
|
||||
|
||||
err_kill_urb:
|
||||
usb_kill_urb(pegasus->irq);
|
||||
cancel_work_sync(&pegasus->init);
|
||||
err_autopm_put:
|
||||
usb_autopm_put_interface(pegasus->intf);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void pegasus_close(struct input_dev *dev)
|
||||
{
|
||||
struct pegasus *pegasus = input_get_drvdata(dev);
|
||||
|
||||
usb_kill_urb(pegasus->irq);
|
||||
cancel_work_sync(&pegasus->init);
|
||||
usb_autopm_put_interface(pegasus->intf);
|
||||
}
|
||||
|
||||
static int pegasus_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct pegasus *pegasus;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
int pipe;
|
||||
|
||||
/* We control interface 0 */
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber >= 1)
|
||||
return -ENODEV;
|
||||
|
||||
/* Sanity check that the device has an endpoint */
|
||||
if (intf->altsetting[0].desc.bNumEndpoints < 1) {
|
||||
dev_err(&intf->dev, "Invalid number of endpoints\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
pegasus = kzalloc(sizeof(*pegasus), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!pegasus || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
pegasus->usbdev = dev;
|
||||
pegasus->dev = input_dev;
|
||||
pegasus->intf = intf;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
pegasus->data_len = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
pegasus->data = usb_alloc_coherent(dev, pegasus->data_len, GFP_KERNEL,
|
||||
&pegasus->data_dma);
|
||||
if (!pegasus->data) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
pegasus->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pegasus->irq) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_dma;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(pegasus->irq, dev, pipe,
|
||||
pegasus->data, pegasus->data_len,
|
||||
pegasus_irq, pegasus, endpoint->bInterval);
|
||||
|
||||
pegasus->irq->transfer_dma = pegasus->data_dma;
|
||||
pegasus->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(pegasus->name, dev->manufacturer,
|
||||
sizeof(pegasus->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(pegasus->name, " ", sizeof(pegasus->name));
|
||||
strlcat(pegasus->name, dev->product, sizeof(pegasus->name));
|
||||
}
|
||||
|
||||
if (!strlen(pegasus->name))
|
||||
snprintf(pegasus->name, sizeof(pegasus->name),
|
||||
"USB Pegasus Device %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(dev, pegasus->phys, sizeof(pegasus->phys));
|
||||
strlcat(pegasus->phys, "/input0", sizeof(pegasus->phys));
|
||||
|
||||
INIT_WORK(&pegasus->init, pegasus_init);
|
||||
|
||||
usb_set_intfdata(intf, pegasus);
|
||||
|
||||
input_dev->name = pegasus->name;
|
||||
input_dev->phys = pegasus->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, pegasus);
|
||||
|
||||
input_dev->open = pegasus_open;
|
||||
input_dev->close = pegasus_close;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
|
||||
__set_bit(ABS_X, input_dev->absbit);
|
||||
__set_bit(ABS_Y, input_dev->absbit);
|
||||
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
|
||||
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
|
||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, -1500, 1500, 8, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 1600, 3000, 8, 0);
|
||||
|
||||
error = input_register_device(pegasus->dev);
|
||||
if (error)
|
||||
goto err_free_urb;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_urb:
|
||||
usb_free_urb(pegasus->irq);
|
||||
err_free_dma:
|
||||
usb_free_coherent(dev, pegasus->data_len,
|
||||
pegasus->data, pegasus->data_dma);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(pegasus);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void pegasus_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct pegasus *pegasus = usb_get_intfdata(intf);
|
||||
|
||||
input_unregister_device(pegasus->dev);
|
||||
|
||||
usb_free_urb(pegasus->irq);
|
||||
usb_free_coherent(interface_to_usbdev(intf),
|
||||
pegasus->data_len, pegasus->data,
|
||||
pegasus->data_dma);
|
||||
|
||||
kfree(pegasus);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
static int pegasus_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct pegasus *pegasus = usb_get_intfdata(intf);
|
||||
|
||||
mutex_lock(&pegasus->dev->mutex);
|
||||
usb_kill_urb(pegasus->irq);
|
||||
cancel_work_sync(&pegasus->init);
|
||||
mutex_unlock(&pegasus->dev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pegasus_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct pegasus *pegasus = usb_get_intfdata(intf);
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock(&pegasus->dev->mutex);
|
||||
if (pegasus->dev->users && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
|
||||
retval = -EIO;
|
||||
mutex_unlock(&pegasus->dev->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int pegasus_reset_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct pegasus *pegasus = usb_get_intfdata(intf);
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock(&pegasus->dev->mutex);
|
||||
if (pegasus->dev->users) {
|
||||
retval = pegasus_set_mode(pegasus, PEN_MODE_XY,
|
||||
NOTETAKER_LED_MOUSE);
|
||||
if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
|
||||
retval = -EIO;
|
||||
}
|
||||
mutex_unlock(&pegasus->dev->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct usb_device_id pegasus_ids[] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_PEGASUSTECH,
|
||||
USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, pegasus_ids);
|
||||
|
||||
static struct usb_driver pegasus_driver = {
|
||||
.name = "pegasus_notetaker",
|
||||
.probe = pegasus_probe,
|
||||
.disconnect = pegasus_disconnect,
|
||||
.suspend = pegasus_suspend,
|
||||
.resume = pegasus_resume,
|
||||
.reset_resume = pegasus_reset_resume,
|
||||
.id_table = pegasus_ids,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(pegasus_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Kepplinger <martink@posteo.de>");
|
||||
MODULE_DESCRIPTION("Pegasus Mobile Notetaker Pen tablet driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -632,7 +632,7 @@ config TOUCHSCREEN_EDT_FT5X06
|
||||
|
||||
config TOUCHSCREEN_MIGOR
|
||||
tristate "Renesas MIGO-R touchscreen"
|
||||
depends on SH_MIGOR && I2C
|
||||
depends on (SH_MIGOR || COMPILE_TEST) && I2C
|
||||
help
|
||||
Say Y here to enable MIGO-R touchscreen support.
|
||||
|
||||
@ -1046,6 +1046,19 @@ config TOUCHSCREEN_PCAP
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pcap_ts.
|
||||
|
||||
config TOUCHSCREEN_RM_TS
|
||||
tristate "Raydium I2C Touchscreen"
|
||||
depends on I2C
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you have Raydium series I2C touchscreen,
|
||||
such as RM32380, connected to your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called raydium_i2c_ts.
|
||||
|
||||
config TOUCHSCREEN_ST1232
|
||||
tristate "Sitronix ST1232 touchscreen controllers"
|
||||
depends on I2C
|
||||
@ -1094,6 +1107,19 @@ config TOUCHSCREEN_SUR40
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sur40.
|
||||
|
||||
config TOUCHSCREEN_SURFACE3_SPI
|
||||
tristate "Ntrig/Microsoft Surface 3 SPI touchscreen"
|
||||
depends on SPI
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you have the Ntrig/Microsoft SPI touchscreen
|
||||
controller chip as found on the Surface 3 in your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called surface3_spi.
|
||||
|
||||
config TOUCHSCREEN_SX8654
|
||||
tristate "Semtech SX8654 touchscreen"
|
||||
depends on I2C
|
||||
|
@ -62,11 +62,13 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_i2c_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
||||
|
@ -595,7 +595,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
|
||||
} else {
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
|
||||
touchscreen_parse_properties(input_dev, false);
|
||||
touchscreen_parse_properties(input_dev, false, NULL);
|
||||
if (!input_abs_get_max(input_dev, ABS_PRESSURE)) {
|
||||
dev_err(dev, "Touchscreen pressure is not specified\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
@ -52,11 +53,7 @@ struct icn8318_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *wake_gpio;
|
||||
u32 max_x;
|
||||
u32 max_y;
|
||||
bool invert_x;
|
||||
bool invert_y;
|
||||
bool swap_x_y;
|
||||
struct touchscreen_properties prop;
|
||||
};
|
||||
|
||||
static int icn8318_read_touch_data(struct i2c_client *client,
|
||||
@ -91,7 +88,7 @@ static irqreturn_t icn8318_irq(int irq, void *dev_id)
|
||||
struct icn8318_data *data = dev_id;
|
||||
struct device *dev = &data->client->dev;
|
||||
struct icn8318_touch_data touch_data;
|
||||
int i, ret, x, y;
|
||||
int i, ret;
|
||||
|
||||
ret = icn8318_read_touch_data(data->client, &touch_data);
|
||||
if (ret < 0) {
|
||||
@ -124,22 +121,9 @@ static irqreturn_t icn8318_irq(int irq, void *dev_id)
|
||||
if (!act)
|
||||
continue;
|
||||
|
||||
x = be16_to_cpu(touch->x);
|
||||
y = be16_to_cpu(touch->y);
|
||||
|
||||
if (data->invert_x)
|
||||
x = data->max_x - x;
|
||||
|
||||
if (data->invert_y)
|
||||
y = data->max_y - y;
|
||||
|
||||
if (!data->swap_x_y) {
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
|
||||
} else {
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
|
||||
}
|
||||
touchscreen_report_pos(data->input, &data->prop,
|
||||
be16_to_cpu(touch->x),
|
||||
be16_to_cpu(touch->y), true);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(data->input);
|
||||
@ -200,10 +184,8 @@ static int icn8318_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct icn8318_data *data;
|
||||
struct input_dev *input;
|
||||
u32 fuzz_x = 0, fuzz_y = 0;
|
||||
int error;
|
||||
|
||||
if (!client->irq) {
|
||||
@ -223,19 +205,6 @@ static int icn8318_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
|
||||
of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
|
||||
dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Optional */
|
||||
of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
|
||||
of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
|
||||
data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
|
||||
data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
|
||||
data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
@ -246,16 +215,14 @@ static int icn8318_probe(struct i2c_client *client,
|
||||
input->close = icn8318_stop;
|
||||
input->dev.parent = dev;
|
||||
|
||||
if (!data->swap_x_y) {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
data->max_x, fuzz_x, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
data->max_y, fuzz_y, 0);
|
||||
} else {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
data->max_y, fuzz_y, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
data->max_x, fuzz_x, 0);
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
|
||||
|
||||
touchscreen_parse_properties(input, true, &data->prop);
|
||||
if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
|
||||
!input_abs_get_max(input, ABS_MT_POSITION_Y)) {
|
||||
dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
|
||||
|
@ -657,7 +657,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
|
||||
|
||||
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
|
||||
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
|
||||
touchscreen_parse_properties(input_dev, true);
|
||||
touchscreen_parse_properties(input_dev, true, NULL);
|
||||
|
||||
error = input_mt_init_slots(input_dev, CY_MAX_ID, 0);
|
||||
if (error) {
|
||||
|
@ -86,6 +86,7 @@ struct edt_reg_addr {
|
||||
struct edt_ft5x06_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct touchscreen_properties prop;
|
||||
u16 num_x;
|
||||
u16 num_y;
|
||||
|
||||
@ -246,8 +247,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
||||
if (!down)
|
||||
continue;
|
||||
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
|
||||
touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y,
|
||||
true);
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(tsdata->input, true);
|
||||
@ -972,7 +973,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
0, tsdata->num_y * 64 - 1, 0, 0);
|
||||
|
||||
touchscreen_parse_properties(input, true);
|
||||
touchscreen_parse_properties(input, true, &tsdata->prop);
|
||||
|
||||
error = input_mt_init_slots(input, tsdata->max_support_points,
|
||||
INPUT_MT_DIRECT);
|
||||
|
@ -202,7 +202,7 @@ static int migor_ts_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int migor_ts_suspend(struct device *dev)
|
||||
static int __maybe_unused migor_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct migor_ts_priv *priv = i2c_get_clientdata(client);
|
||||
@ -213,7 +213,7 @@ static int migor_ts_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int migor_ts_resume(struct device *dev)
|
||||
static int __maybe_unused migor_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct migor_ts_priv *priv = i2c_get_clientdata(client);
|
||||
@ -230,7 +230,7 @@ static const struct i2c_device_id migor_ts_id[] = {
|
||||
{ "migor_ts", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, migor_ts);
|
||||
MODULE_DEVICE_TABLE(i2c, migor_ts_id);
|
||||
|
||||
static struct i2c_driver migor_ts_driver = {
|
||||
.driver = {
|
||||
|
@ -55,12 +55,16 @@ static void touchscreen_set_params(struct input_dev *dev,
|
||||
* @input: input device that should be parsed
|
||||
* @multitouch: specifies whether parsed properties should be applied to
|
||||
* single-touch or multi-touch axes
|
||||
* @prop: pointer to a struct touchscreen_properties into which to store
|
||||
* axis swap and invert info for use with touchscreen_report_x_y();
|
||||
* or %NULL
|
||||
*
|
||||
* This function parses common DT properties for touchscreens and setups the
|
||||
* input device accordingly. The function keeps previously set up default
|
||||
* values if no value is specified via DT.
|
||||
*/
|
||||
void touchscreen_parse_properties(struct input_dev *input, bool multitouch)
|
||||
void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
|
||||
struct touchscreen_properties *prop)
|
||||
{
|
||||
struct device *dev = input->dev.parent;
|
||||
unsigned int axis;
|
||||
@ -104,5 +108,80 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch)
|
||||
&fuzz);
|
||||
if (data_present)
|
||||
touchscreen_set_params(input, axis, maximum, fuzz);
|
||||
|
||||
if (!prop)
|
||||
return;
|
||||
|
||||
axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
|
||||
|
||||
prop->max_x = input_abs_get_max(input, axis);
|
||||
prop->max_y = input_abs_get_max(input, axis + 1);
|
||||
prop->invert_x =
|
||||
device_property_read_bool(dev, "touchscreen-inverted-x");
|
||||
prop->invert_y =
|
||||
device_property_read_bool(dev, "touchscreen-inverted-y");
|
||||
prop->swap_x_y =
|
||||
device_property_read_bool(dev, "touchscreen-swapped-x-y");
|
||||
|
||||
if (prop->swap_x_y)
|
||||
swap(input->absinfo[axis], input->absinfo[axis + 1]);
|
||||
}
|
||||
EXPORT_SYMBOL(touchscreen_parse_properties);
|
||||
|
||||
static void
|
||||
touchscreen_apply_prop_to_x_y(const struct touchscreen_properties *prop,
|
||||
unsigned int *x, unsigned int *y)
|
||||
{
|
||||
if (prop->invert_x)
|
||||
*x = prop->max_x - *x;
|
||||
|
||||
if (prop->invert_y)
|
||||
*y = prop->max_y - *y;
|
||||
|
||||
if (prop->swap_x_y)
|
||||
swap(*x, *y);
|
||||
}
|
||||
|
||||
/**
|
||||
* touchscreen_set_mt_pos - Set input_mt_pos coordinates
|
||||
* @pos: input_mt_pos to set coordinates of
|
||||
* @prop: pointer to a struct touchscreen_properties
|
||||
* @x: X coordinate to store in pos
|
||||
* @y: Y coordinate to store in pos
|
||||
*
|
||||
* Adjust the passed in x and y values applying any axis inversion and
|
||||
* swapping requested in the passed in touchscreen_properties and store
|
||||
* the result in a struct input_mt_pos.
|
||||
*/
|
||||
void touchscreen_set_mt_pos(struct input_mt_pos *pos,
|
||||
const struct touchscreen_properties *prop,
|
||||
unsigned int x, unsigned int y)
|
||||
{
|
||||
touchscreen_apply_prop_to_x_y(prop, &x, &y);
|
||||
pos->x = x;
|
||||
pos->y = y;
|
||||
}
|
||||
EXPORT_SYMBOL(touchscreen_set_mt_pos);
|
||||
|
||||
/**
|
||||
* touchscreen_report_pos - Report touchscreen coordinates
|
||||
* @input: input_device to report coordinates for
|
||||
* @prop: pointer to a struct touchscreen_properties
|
||||
* @x: X coordinate to report
|
||||
* @y: Y coordinate to report
|
||||
* @multitouch: Report coordinates on single-touch or multi-touch axes
|
||||
*
|
||||
* Adjust the passed in x and y values applying any axis inversion and
|
||||
* swapping requested in the passed in touchscreen_properties and then
|
||||
* report the resulting coordinates on the input_dev's x and y axis.
|
||||
*/
|
||||
void touchscreen_report_pos(struct input_dev *input,
|
||||
const struct touchscreen_properties *prop,
|
||||
unsigned int x, unsigned int y,
|
||||
bool multitouch)
|
||||
{
|
||||
touchscreen_apply_prop_to_x_y(prop, &x, &y);
|
||||
input_report_abs(input, multitouch ? ABS_MT_POSITION_X : ABS_X, x);
|
||||
input_report_abs(input, multitouch ? ABS_MT_POSITION_Y : ABS_Y, y);
|
||||
}
|
||||
EXPORT_SYMBOL(touchscreen_report_pos);
|
||||
|
@ -27,9 +27,9 @@
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
/*#include <linux/of.h>*/
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_data/pixcir_i2c_ts.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */
|
||||
|
||||
@ -41,19 +41,15 @@ struct pixcir_i2c_ts_data {
|
||||
struct gpio_desc *gpio_enable;
|
||||
struct gpio_desc *gpio_wake;
|
||||
const struct pixcir_i2c_chip_data *chip;
|
||||
struct touchscreen_properties prop;
|
||||
int max_fingers; /* Max fingers supported in this instance */
|
||||
bool running;
|
||||
};
|
||||
|
||||
struct pixcir_touch {
|
||||
int x;
|
||||
int y;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct pixcir_report_data {
|
||||
int num_touches;
|
||||
struct pixcir_touch touches[PIXCIR_MAX_SLOTS];
|
||||
struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
|
||||
int ids[PIXCIR_MAX_SLOTS];
|
||||
};
|
||||
|
||||
static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
|
||||
@ -98,11 +94,11 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
|
||||
bufptr = &rdbuf[2];
|
||||
|
||||
for (i = 0; i < touch; i++) {
|
||||
report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
|
||||
report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
|
||||
|
||||
touchscreen_set_mt_pos(&report->pos[i], &tsdata->prop,
|
||||
get_unaligned_le16(bufptr),
|
||||
get_unaligned_le16(bufptr + 2));
|
||||
if (chip->has_hw_ids) {
|
||||
report->touches[i].id = bufptr[4];
|
||||
report->ids[i] = bufptr[4];
|
||||
bufptr = bufptr + 5;
|
||||
} else {
|
||||
bufptr = bufptr + 4;
|
||||
@ -113,9 +109,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
|
||||
static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||
struct pixcir_report_data *report)
|
||||
{
|
||||
struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
|
||||
int slots[PIXCIR_MAX_SLOTS];
|
||||
struct pixcir_touch *touch;
|
||||
int n, i, slot;
|
||||
struct device *dev = &ts->client->dev;
|
||||
const struct pixcir_i2c_chip_data *chip = ts->chip;
|
||||
@ -124,24 +118,16 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||
if (n > PIXCIR_MAX_SLOTS)
|
||||
n = PIXCIR_MAX_SLOTS;
|
||||
|
||||
if (!ts->chip->has_hw_ids) {
|
||||
for (i = 0; i < n; i++) {
|
||||
touch = &report->touches[i];
|
||||
pos[i].x = touch->x;
|
||||
pos[i].y = touch->y;
|
||||
}
|
||||
|
||||
input_mt_assign_slots(ts->input, slots, pos, n, 0);
|
||||
}
|
||||
if (!ts->chip->has_hw_ids)
|
||||
input_mt_assign_slots(ts->input, slots, report->pos, n, 0);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
touch = &report->touches[i];
|
||||
|
||||
if (chip->has_hw_ids) {
|
||||
slot = input_mt_get_slot_by_key(ts->input, touch->id);
|
||||
slot = input_mt_get_slot_by_key(ts->input,
|
||||
report->ids[i]);
|
||||
if (slot < 0) {
|
||||
dev_dbg(dev, "no free slot for id 0x%x\n",
|
||||
touch->id);
|
||||
report->ids[i]);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
@ -149,14 +135,15 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||
}
|
||||
|
||||
input_mt_slot(ts->input, slot);
|
||||
input_mt_report_slot_state(ts->input,
|
||||
MT_TOOL_FINGER, true);
|
||||
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
|
||||
|
||||
input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x);
|
||||
input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y);
|
||||
input_report_abs(ts->input, ABS_MT_POSITION_X,
|
||||
report->pos[i].x);
|
||||
input_report_abs(ts->input, ABS_MT_POSITION_Y,
|
||||
report->pos[i].y);
|
||||
|
||||
dev_dbg(dev, "%d: slot %d, x %d, y %d\n",
|
||||
i, slot, touch->x, touch->y);
|
||||
i, slot, report->pos[i].x, report->pos[i].y);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(ts->input);
|
||||
@ -515,7 +502,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
} else {
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
|
||||
touchscreen_parse_properties(input, true);
|
||||
touchscreen_parse_properties(input, true, &tsdata->prop);
|
||||
if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
|
||||
!input_abs_get_max(input, ABS_MT_POSITION_Y)) {
|
||||
dev_err(dev, "Touchscreen size is not specified\n");
|
||||
|
1238
drivers/input/touchscreen/raydium_i2c_ts.c
Normal file
1238
drivers/input/touchscreen/raydium_i2c_ts.c
Normal file
File diff suppressed because it is too large
Load Diff
427
drivers/input/touchscreen/surface3_spi.c
Normal file
427
drivers/input/touchscreen/surface3_spi.c
Normal file
@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Driver for Ntrig/Microsoft Touchscreens over SPI
|
||||
*
|
||||
* Copyright (c) 2016 Red Hat Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define SURFACE3_PACKET_SIZE 264
|
||||
|
||||
#define SURFACE3_REPORT_TOUCH 0xd2
|
||||
#define SURFACE3_REPORT_PEN 0x16
|
||||
|
||||
struct surface3_ts_data {
|
||||
struct spi_device *spi;
|
||||
struct gpio_desc *gpiod_rst[2];
|
||||
struct input_dev *input_dev;
|
||||
struct input_dev *pen_input_dev;
|
||||
int pen_tool;
|
||||
|
||||
u8 rd_buf[SURFACE3_PACKET_SIZE] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct surface3_ts_data_finger {
|
||||
u8 status;
|
||||
__le16 tracking_id;
|
||||
__le16 x;
|
||||
__le16 cx;
|
||||
__le16 y;
|
||||
__le16 cy;
|
||||
__le16 width;
|
||||
__le16 height;
|
||||
u32 padding;
|
||||
} __packed;
|
||||
|
||||
struct surface3_ts_data_pen {
|
||||
u8 status;
|
||||
__le16 x;
|
||||
__le16 y;
|
||||
__le16 pressure;
|
||||
u8 padding;
|
||||
} __packed;
|
||||
|
||||
static int surface3_spi_read(struct surface3_ts_data *ts_data)
|
||||
{
|
||||
struct spi_device *spi = ts_data->spi;
|
||||
|
||||
memset(ts_data->rd_buf, 0, sizeof(ts_data->rd_buf));
|
||||
return spi_read(spi, ts_data->rd_buf, sizeof(ts_data->rd_buf));
|
||||
}
|
||||
|
||||
static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,
|
||||
struct surface3_ts_data_finger *finger)
|
||||
{
|
||||
int st = finger->status & 0x01;
|
||||
int slot;
|
||||
|
||||
slot = input_mt_get_slot_by_key(ts_data->input_dev,
|
||||
get_unaligned_le16(&finger->tracking_id));
|
||||
if (slot < 0)
|
||||
return;
|
||||
|
||||
input_mt_slot(ts_data->input_dev, slot);
|
||||
input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, st);
|
||||
if (st) {
|
||||
input_report_abs(ts_data->input_dev,
|
||||
ABS_MT_POSITION_X,
|
||||
get_unaligned_le16(&finger->x));
|
||||
input_report_abs(ts_data->input_dev,
|
||||
ABS_MT_POSITION_Y,
|
||||
get_unaligned_le16(&finger->y));
|
||||
input_report_abs(ts_data->input_dev,
|
||||
ABS_MT_WIDTH_MAJOR,
|
||||
get_unaligned_le16(&finger->width));
|
||||
input_report_abs(ts_data->input_dev,
|
||||
ABS_MT_WIDTH_MINOR,
|
||||
get_unaligned_le16(&finger->height));
|
||||
}
|
||||
}
|
||||
|
||||
static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data)
|
||||
{
|
||||
u16 timestamp;
|
||||
unsigned int i;
|
||||
timestamp = get_unaligned_le16(&data[15]);
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
struct surface3_ts_data_finger *finger;
|
||||
|
||||
finger = (struct surface3_ts_data_finger *)&data[17 +
|
||||
i * sizeof(struct surface3_ts_data_finger)];
|
||||
|
||||
/*
|
||||
* When bit 5 of status is 1, it marks the end of the report:
|
||||
* - touch present: 0xe7
|
||||
* - touch released: 0xe4
|
||||
* - nothing valuable: 0xff
|
||||
*/
|
||||
if (finger->status & 0x10)
|
||||
break;
|
||||
|
||||
surface3_spi_report_touch(ts_data, finger);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(ts_data->input_dev);
|
||||
input_sync(ts_data->input_dev);
|
||||
}
|
||||
|
||||
static void surface3_spi_report_pen(struct surface3_ts_data *ts_data,
|
||||
struct surface3_ts_data_pen *pen)
|
||||
{
|
||||
struct input_dev *dev = ts_data->pen_input_dev;
|
||||
int st = pen->status;
|
||||
int prox = st & 0x01;
|
||||
int rubber = st & 0x18;
|
||||
int tool = (prox && rubber) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
|
||||
/* fake proximity out to switch tools */
|
||||
if (ts_data->pen_tool != tool) {
|
||||
input_report_key(dev, ts_data->pen_tool, 0);
|
||||
input_sync(dev);
|
||||
ts_data->pen_tool = tool;
|
||||
}
|
||||
|
||||
input_report_key(dev, BTN_TOUCH, st & 0x12);
|
||||
|
||||
input_report_key(dev, ts_data->pen_tool, prox);
|
||||
|
||||
if (st) {
|
||||
input_report_key(dev,
|
||||
BTN_STYLUS,
|
||||
st & 0x04);
|
||||
|
||||
input_report_abs(dev,
|
||||
ABS_X,
|
||||
get_unaligned_le16(&pen->x));
|
||||
input_report_abs(dev,
|
||||
ABS_Y,
|
||||
get_unaligned_le16(&pen->y));
|
||||
input_report_abs(dev,
|
||||
ABS_PRESSURE,
|
||||
get_unaligned_le16(&pen->pressure));
|
||||
}
|
||||
}
|
||||
|
||||
static void surface3_spi_process_pen(struct surface3_ts_data *ts_data, u8 *data)
|
||||
{
|
||||
struct surface3_ts_data_pen *pen;
|
||||
|
||||
pen = (struct surface3_ts_data_pen *)&data[15];
|
||||
|
||||
surface3_spi_report_pen(ts_data, pen);
|
||||
input_sync(ts_data->pen_input_dev);
|
||||
}
|
||||
|
||||
static void surface3_spi_process(struct surface3_ts_data *ts_data)
|
||||
{
|
||||
const char header[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xa5, 0x5a, 0xe7, 0x7e, 0x01
|
||||
};
|
||||
u8 *data = ts_data->rd_buf;
|
||||
|
||||
if (memcmp(header, data, sizeof(header)))
|
||||
dev_err(&ts_data->spi->dev,
|
||||
"%s header error: %*ph, ignoring...\n",
|
||||
__func__, (int)sizeof(header), data);
|
||||
|
||||
switch (data[9]) {
|
||||
case SURFACE3_REPORT_TOUCH:
|
||||
surface3_spi_process_touch(ts_data, data);
|
||||
break;
|
||||
case SURFACE3_REPORT_PEN:
|
||||
surface3_spi_process_pen(ts_data, data);
|
||||
break;
|
||||
default:
|
||||
dev_err(&ts_data->spi->dev,
|
||||
"%s unknown packet type: %x, ignoring...\n",
|
||||
__func__, data[9]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct surface3_ts_data *data = dev_id;
|
||||
|
||||
if (surface3_spi_read(data))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
dev_dbg(&data->spi->dev, "%s received -> %*ph\n",
|
||||
__func__, SURFACE3_PACKET_SIZE, data->rd_buf);
|
||||
surface3_spi_process(data);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void surface3_spi_power(struct surface3_ts_data *data, bool on)
|
||||
{
|
||||
gpiod_set_value(data->gpiod_rst[0], on);
|
||||
gpiod_set_value(data->gpiod_rst[1], on);
|
||||
/* let the device settle a little */
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
/**
|
||||
* surface3_spi_get_gpio_config - Get GPIO config from ACPI/DT
|
||||
*
|
||||
* @ts: surface3_spi_ts_data pointer
|
||||
*/
|
||||
static int surface3_spi_get_gpio_config(struct surface3_ts_data *data)
|
||||
{
|
||||
int error;
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpiod;
|
||||
int i;
|
||||
|
||||
dev = &data->spi->dev;
|
||||
|
||||
/* Get the reset lines GPIO pin number */
|
||||
for (i = 0; i < 2; i++) {
|
||||
gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpiod)) {
|
||||
error = PTR_ERR(gpiod);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev,
|
||||
"Failed to get power GPIO %d: %d\n",
|
||||
i,
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
data->gpiod_rst[i] = gpiod;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int surface3_spi_create_touch_input(struct surface3_ts_data *data)
|
||||
{
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
input = devm_input_allocate_device(&data->spi->dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
data->input_dev = input;
|
||||
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, 9600, 0, 0);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X, 40);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 7200, 0, 0);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y, 48);
|
||||
input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 1024, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 1024, 0, 0);
|
||||
input_mt_init_slots(input, 10, INPUT_MT_DIRECT);
|
||||
|
||||
input->name = "Surface3 SPI Capacitive TouchScreen";
|
||||
input->phys = "input/ts";
|
||||
input->id.bustype = BUS_SPI;
|
||||
input->id.vendor = 0x045e; /* Microsoft */
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = 0x0000;
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&data->spi->dev,
|
||||
"Failed to register input device: %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int surface3_spi_create_pen_input(struct surface3_ts_data *data)
|
||||
{
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
input = devm_input_allocate_device(&data->spi->dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
data->pen_input_dev = input;
|
||||
data->pen_tool = BTN_TOOL_PEN;
|
||||
|
||||
__set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||
input_set_abs_params(input, ABS_X, 0, 9600, 0, 0);
|
||||
input_abs_set_res(input, ABS_X, 40);
|
||||
input_set_abs_params(input, ABS_Y, 0, 7200, 0, 0);
|
||||
input_abs_set_res(input, ABS_Y, 48);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 1024, 0, 0);
|
||||
input_set_capability(input, EV_KEY, BTN_TOUCH);
|
||||
input_set_capability(input, EV_KEY, BTN_STYLUS);
|
||||
input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
|
||||
input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
|
||||
|
||||
input->name = "Surface3 SPI Pen Input";
|
||||
input->phys = "input/ts";
|
||||
input->id.bustype = BUS_SPI;
|
||||
input->id.vendor = 0x045e; /* Microsoft */
|
||||
input->id.product = 0x0002;
|
||||
input->id.version = 0x0000;
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&data->spi->dev,
|
||||
"Failed to register input device: %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int surface3_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct surface3_ts_data *data;
|
||||
int error;
|
||||
|
||||
/* Set up SPI*/
|
||||
spi->bits_per_word = 8;
|
||||
spi->mode = SPI_MODE_0;
|
||||
error = spi_setup(spi);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->spi = spi;
|
||||
spi_set_drvdata(spi, data);
|
||||
|
||||
error = surface3_spi_get_gpio_config(data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
surface3_spi_power(data, true);
|
||||
surface3_spi_power(data, false);
|
||||
surface3_spi_power(data, true);
|
||||
|
||||
error = surface3_spi_create_touch_input(data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = surface3_spi_create_pen_input(data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
NULL, surface3_spi_irq_handler,
|
||||
IRQF_ONESHOT,
|
||||
"Surface3-irq", data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused surface3_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct surface3_ts_data *data = spi_get_drvdata(spi);
|
||||
|
||||
disable_irq(data->spi->irq);
|
||||
|
||||
surface3_spi_power(data, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused surface3_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct surface3_ts_data *data = spi_get_drvdata(spi);
|
||||
|
||||
surface3_spi_power(data, true);
|
||||
|
||||
enable_irq(data->spi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(surface3_spi_pm_ops,
|
||||
surface3_spi_suspend,
|
||||
surface3_spi_resume);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id surface3_spi_acpi_match[] = {
|
||||
{ "MSHW0037", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, surface3_spi_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct spi_driver surface3_spi_driver = {
|
||||
.driver = {
|
||||
.name = "Surface3-spi",
|
||||
.acpi_match_table = ACPI_PTR(surface3_spi_acpi_match),
|
||||
.pm = &surface3_spi_pm_ops,
|
||||
},
|
||||
.probe = surface3_spi_probe,
|
||||
};
|
||||
|
||||
module_spi_driver(surface3_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
|
||||
MODULE_DESCRIPTION("Surface 3 SPI touchscreen driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -406,7 +406,7 @@ static int titsc_probe(struct platform_device *pdev)
|
||||
int err;
|
||||
|
||||
/* Allocate memory for device */
|
||||
ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL);
|
||||
ts_dev = kzalloc(sizeof(*ts_dev), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts_dev || !input_dev) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory.\n");
|
||||
|
@ -568,7 +568,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_p, fudge_p, 0);
|
||||
|
||||
if (np)
|
||||
touchscreen_parse_properties(input_dev, false);
|
||||
touchscreen_parse_properties(input_dev, false, NULL);
|
||||
|
||||
input_dev->open = tsc200x_open;
|
||||
input_dev->close = tsc200x_close;
|
||||
|
@ -518,13 +518,21 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename,
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
error = input_mt_init_slots(dev, 2, 0);
|
||||
if (error) {
|
||||
dev_err(&w8001->serio->dev,
|
||||
"failed to initialize MT slots: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X,
|
||||
0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y,
|
||||
0, touch.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_MAX, 0, 0);
|
||||
input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res);
|
||||
input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res);
|
||||
|
||||
strlcat(basename, " 2FG", basename_sz);
|
||||
if (w8001->max_pen_x && w8001->max_pen_y)
|
||||
|
@ -95,7 +95,7 @@ struct input_value {
|
||||
* @grab: input handle that currently has the device grabbed (via
|
||||
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
|
||||
* recipient for all input events coming from the device
|
||||
* @event_lock: this spinlock is is taken when input core receives
|
||||
* @event_lock: this spinlock is taken when input core receives
|
||||
* and processes a new event for the device (in input_event()).
|
||||
* Code that accesses and/or modifies parameters of a device
|
||||
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
|
||||
|
@ -10,7 +10,26 @@
|
||||
#define _TOUCHSCREEN_H
|
||||
|
||||
struct input_dev;
|
||||
struct input_mt_pos;
|
||||
|
||||
void touchscreen_parse_properties(struct input_dev *dev, bool multitouch);
|
||||
struct touchscreen_properties {
|
||||
unsigned int max_x;
|
||||
unsigned int max_y;
|
||||
bool invert_x;
|
||||
bool invert_y;
|
||||
bool swap_x_y;
|
||||
};
|
||||
|
||||
void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
|
||||
struct touchscreen_properties *prop);
|
||||
|
||||
void touchscreen_set_mt_pos(struct input_mt_pos *pos,
|
||||
const struct touchscreen_properties *prop,
|
||||
unsigned int x, unsigned int y);
|
||||
|
||||
void touchscreen_report_pos(struct input_dev *input,
|
||||
const struct touchscreen_properties *prop,
|
||||
unsigned int x, unsigned int y,
|
||||
bool multitouch);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user