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:
Linus Torvalds 2016-07-27 14:30:41 -07:00
commit d85486d471
41 changed files with 2928 additions and 129 deletions

View 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;
};

View 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>;
};

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View 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");

View 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");

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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

View 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");

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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) {

View File

@ -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);

View File

@ -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 = {

View File

@ -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);

View File

@ -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");

File diff suppressed because it is too large Load Diff

View 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");

View File

@ -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");

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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