Commit d85486d4 authored by Linus Torvalds's avatar Linus Torvalds

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"
  ...
parents 66304207 08088828
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;
};
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;
}
......
/*
* 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");
/*
* 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 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
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
/*
* 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");
......
/*
* Raydium touchscreen I2C driver.
*
* Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2, and only version 2, as published by the
* Free Software Foundation.
*
* 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.
*
* Raydium reserves the right to make changes without further notice
* to the materials described herein. Raydium does not assume any
* liability arising out of the application described herein.
*
* Contact Raydium Semiconductor Corporation at www.rad-ic.com
*/
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
/* Slave I2C mode */
#define RM_BOOT_BLDR 0x02
#define RM_BOOT_MAIN 0x03
/* I2C bootoloader commands */
#define RM_CMD_BOOT_PAGE_WRT 0x0B /* send bl page write */
#define RM_CMD_BOOT_WRT 0x11 /* send bl write */
#define RM_CMD_BOOT_ACK 0x22 /* send ack*/
#define RM_CMD_BOOT_CHK 0x33 /* send data check */
#define RM_CMD_BOOT_READ 0x44 /* send wait bl data ready*/
#define RM_BOOT_RDY 0xFF /* bl data ready */
/* I2C main commands */
#define RM_CMD_QUERY_BANK 0x2B
#define RM_CMD_DATA_BANK 0x4D
#define RM_CMD_ENTER_SLEEP 0x4E
#define RM_CMD_BANK_SWITCH 0xAA
#define RM_RESET_MSG_ADDR 0x40000004
#define RM_MAX_READ_SIZE 56
#define RM_PACKET_CRC_SIZE 2
/* Touch relative info */
#define RM_MAX_RETRIES 3
#define RM_MAX_TOUCH_NUM 10
#define RM_BOOT_DELAY_MS 100
/* Offsets in contact data */
#define RM_CONTACT_STATE_POS 0
#define RM_CONTACT_X_POS 1
#define RM_CONTACT_Y_POS 3
#define RM_CONTACT_PRESSURE_POS 5
#define RM_CONTACT_WIDTH_X_POS 6
#define RM_CONTACT_WIDTH_Y_POS 7
/* Bootloader relative info */
#define RM_BL_WRT_CMD_SIZE 3 /* bl flash wrt cmd size */
#define RM_BL_WRT_PKG_SIZE 32 /* bl wrt pkg size */
#define RM_BL_WRT_LEN (RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
#define RM_FW_PAGE_SIZE 128
#define RM_MAX_FW_RETRIES 30
#define RM_MAX_FW_SIZE 0xD000
#define RM_POWERON_DELAY_USEC 500
#define RM_RESET_DELAY_MSEC 50
enum raydium_bl_cmd {
BL_HEADER = 0,
BL_PAGE_STR,
BL_PKG_IDX,
BL_DATA_STR,
};
enum raydium_bl_ack {
RAYDIUM_ACK_NULL = 0,
RAYDIUM_WAIT_READY,
RAYDIUM_PATH_READY,
};
enum raydium_boot_mode {
RAYDIUM_TS_MAIN = 0,
RAYDIUM_TS_BLDR,
};
/* Response to RM_CMD_DATA_BANK request */
struct raydium_data_info {
__le32 data_bank_addr;
u8 pkg_size;
u8 tp_info_size;
};
struct raydium_info {
__le32 hw_ver; /*device version */
u8 main_ver;
u8 sub_ver;
__le16 ft_ver; /* test version */
u8 x_num;
u8 y_num;
__le16 x_max;
__le16 y_max;
u8 x_res; /* units/mm */
u8 y_res; /* units/mm */
};
/* struct raydium_data - represents state of Raydium touchscreen device */
struct raydium_data {
struct i2c_client *client;
struct input_dev *input;
struct regulator *avdd;
struct regulator *vccio;
struct gpio_desc *reset_gpio;
struct raydium_info info;
struct mutex sysfs_mutex;
u8 *report_data;
u32 data_bank_addr;
u8 report_size;
u8 contact_size;
u8 pkg_size;
enum raydium_boot_mode boot_mode;
bool wake_irq_enabled;
};
static int raydium_i2c_send(struct i2c_client *client,
u8 addr, const void *data, size_t len)
{
u8 *buf;
int tries = 0;
int ret;
buf = kmalloc(len + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = addr;
memcpy(buf + 1, data, len);
do {
ret = i2c_master_send(client, buf, len + 1);
if (likely(ret == len + 1))
break;
msleep(20);
} while (++tries < RM_MAX_RETRIES);
kfree(buf);
if (unlikely(ret != len + 1)) {
if (ret >= 0)
ret = -EIO;
dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
return ret;
}
return 0;
}
static int raydium_i2c_read(struct i2c_client *client,
u8 addr, void *data, size_t len)
{
struct i2c_msg xfer[] = {
{
.addr = client->addr,
.len = 1,
.buf = &addr,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = data,
}
};
int ret;
ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
if (unlikely(ret != ARRAY_SIZE(xfer)))
return ret < 0 ? ret : -EIO;
return 0;
}
static int raydium_i2c_read_message(struct i2c_client *client,
u32 addr, void *data, size_t len)
{
__be32 be_addr;
size_t xfer_len;
int error;
while (len) {
xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
be_addr = cpu_to_be32(addr);
error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
&be_addr, sizeof(be_addr));
if (!error)
error = raydium_i2c_read(client, addr & 0xff,
data, xfer_len);
if (error)
return error;
len -= xfer_len;
data += xfer_len;
addr += xfer_len;
}
return 0;
}
static int raydium_i2c_send_message(struct i2c_client *client,
u32 addr, const void *data, size_t len)
{
__be32 be_addr = cpu_to_be32(addr);
int error;
error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
&be_addr, sizeof(be_addr));
if (!error)
error = raydium_i2c_send(client, addr & 0xff, data, len);
return error;
}
static int raydium_i2c_sw_reset(struct i2c_client *client)
{
const u8 soft_rst_cmd = 0x01;
int error;
error = raydium_i2c_send_message(client, RM_RESET_MSG_ADDR,
&soft_rst_cmd, sizeof(soft_rst_cmd));
if (error) {
dev_err(&client->dev, "software reset failed: %d\n", error);
return error;
}
msleep(RM_RESET_DELAY_MSEC);
return 0;
}
static int raydium_i2c_query_ts_info(struct raydium_data *ts)
{
struct i2c_client *client = ts->client;
struct raydium_data_info data_info;
__le32 query_bank_addr;
int error, retry_cnt;
for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
error = raydium_i2c_read(client, RM_CMD_DATA_BANK,
&data_info, sizeof(data_info));
if (error)
continue;
/*
* Warn user if we already allocated memory for reports and
* then the size changed (due to firmware update?) and keep
* old size instead.
*/
if (ts->report_data && ts->pkg_size != data_info.pkg_size) {
dev_warn(&client->dev,
"report size changes, was: %d, new: %d\n",
ts->pkg_size, data_info.pkg_size);
} else {
ts->pkg_size = data_info.pkg_size;
ts->report_size = ts->pkg_size - RM_PACKET_CRC_SIZE;
}
ts->contact_size = data_info.tp_info_size;
ts->data_bank_addr = le32_to_cpu(data_info.data_bank_addr);
dev_dbg(&client->dev,
"data_bank_addr: %#08x, report_size: %d, contact_size: %d\n",
ts->data_bank_addr, ts->report_size, ts->contact_size);
error = raydium_i2c_read(client, RM_CMD_QUERY_BANK,
&query_bank_addr,
sizeof(query_bank_addr));
if (error)
continue;
error = raydium_i2c_read_message(client,
le32_to_cpu(query_bank_addr),
&ts->info, sizeof(ts->info));
if (error)
continue;
return 0;
}
dev_err(&client->dev, "failed to query device parameters: %d\n", error);
return error;
}
static int raydium_i2c_check_fw_status(struct raydium_data *ts)
{
struct i2c_client *client = ts->client;
static const u8 bl_ack = 0x62;
static const u8 main_ack = 0x66;
u8 buf[4];
int error;
error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
if (!error) {
if (buf[0] == bl_ack)
ts->boot_mode = RAYDIUM_TS_BLDR;
else if (buf[0] == main_ack)
ts->boot_mode = RAYDIUM_TS_MAIN;
return 0;
}
return error;
}
static int raydium_i2c_initialize(struct raydium_data *ts)
{
struct i2c_client *client = ts->client;
int error, retry_cnt;
for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
/* Wait for Hello packet */
msleep(RM_BOOT_DELAY_MS);
error = raydium_i2c_check_fw_status(ts);
if (error) {
dev_err(&client->dev,
"failed to read 'hello' packet: %d\n", error);
continue;
}
if (ts->boot_mode == RAYDIUM_TS_BLDR ||
ts->boot_mode == RAYDIUM_TS_MAIN) {
break;
}
}
if (error)
ts->boot_mode = RAYDIUM_TS_BLDR;
if (ts->boot_mode == RAYDIUM_TS_BLDR) {
ts->info.hw_ver = cpu_to_le32(0xffffffffUL);
ts->info.main_ver = 0xff;
ts->info.sub_ver = 0xff;
} else {
raydium_i2c_query_ts_info(ts);
}
return error;
}
static int raydium_i2c_bl_chk_state(struct i2c_client *client,
enum raydium_bl_ack state)
{
static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
u8 rbuf[sizeof(ack_ok)];
u8 retry;
int error;
for (retry = 0; retry < RM_MAX_FW_RETRIES; retry++) {
switch (state) {
case RAYDIUM_ACK_NULL:
return 0;
case RAYDIUM_WAIT_READY:
error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
&rbuf[0], 1);
if (!error && rbuf[0] == RM_BOOT_RDY)
return 0;
break;
case RAYDIUM_PATH_READY:
error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
rbuf, sizeof(rbuf));
if (!error && !memcmp(rbuf, ack_ok, sizeof(ack_ok)))
return 0;
break;
default:
dev_err(&client->dev, "%s: invalid target state %d\n",
__func__, state);
return -EINVAL;
}
msleep(20);
}
return -ETIMEDOUT;
}
static int raydium_i2c_write_object(struct i2c_client *client,
const void *data, size_t len,
enum raydium_bl_ack state)
{
int error;
error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len);
if (error) {
dev_err(&client->dev, "WRT obj command failed: %d\n",
error);
return error;
}
error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0);
if (error) {
dev_err(&client->dev, "Ack obj command failed: %d\n", error);
return error;
}
error = raydium_i2c_bl_chk_state(client, state);
if (error) {
dev_err(&client->dev, "BL check state failed: %d\n", error);
return error;
}
return 0;
}
static bool raydium_i2c_boot_trigger(struct i2c_client *client)
{
static const u8 cmd[7][6] = {
{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7 },
{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
{ 0x08, 0x04, 0x09, 0x00, 0x50, 0x00 },
{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0x00 },
{ 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0xA2, 0x00, 0x00, 0x00, 0x00 },
};
int i;
int error;
for (i = 0; i < 7; i++) {
error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
RAYDIUM_WAIT_READY);
if (error) {
dev_err(&client->dev,
"boot trigger failed at step %d: %d\n",
i, error);
return error;
}
}
return 0;
}
static bool raydium_i2c_fw_trigger(struct i2c_client *client)
{
static const u8 cmd[5][11] = {
{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0 },
{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
};
int i;
int error;
for (i = 0; i < 5; i++) {
error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
RAYDIUM_ACK_NULL);
if (error) {
dev_err(&client->dev,
"fw trigger failed at step %d: %d\n",
i, error);
return error;
}
}
return 0;
}
static int raydium_i2c_check_path(struct i2c_client *client)
{
static const u8 cmd[] = { 0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00 };
int error;
error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
RAYDIUM_PATH_READY);
if (error) {
dev_err(&client->dev, "check path command failed: %d\n", error);
return error;
}
return 0;
}
static int raydium_i2c_enter_bl(struct i2c_client *client)
{
static const u8 cal_cmd[] = { 0x00, 0x01, 0x52 };
int error;
error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
RAYDIUM_ACK_NULL);
if (error) {
dev_err(&client->dev, "enter bl command failed: %d\n", error);
return error;
}
msleep(RM_BOOT_DELAY_MS);
return 0;
}
static int raydium_i2c_leave_bl(struct i2c_client *client)
{
static const u8 leave_cmd[] = { 0x05, 0x00 };
int error;
error = raydium_i2c_write_object(client, leave_cmd, sizeof(leave_cmd),
RAYDIUM_ACK_NULL);
if (error) {
dev_err(&client->dev, "leave bl command failed: %d\n", error);
return error;
}
msleep(RM_BOOT_DELAY_MS);
return 0;
}
static int raydium_i2c_write_checksum(struct i2c_client *client,
size_t length, u16 checksum)
{
u8 checksum_cmd[] = { 0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00 };
int error;
put_unaligned_le16(length, &checksum_cmd[3]);
put_unaligned_le16(checksum, &checksum_cmd[5]);
error = raydium_i2c_write_object(client,
checksum_cmd, sizeof(checksum_cmd),
RAYDIUM_ACK_NULL);
if (error) {
dev_err(&client->dev, "failed to write checksum: %d\n",
error);
return error;
}
return 0;
}
static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
{
static const u8 cmd[] = { 0x0A, 0xAA };
int error;
error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
RAYDIUM_WAIT_READY);
if (error) {
dev_err(&client->dev, "disable watchdog command failed: %d\n",
error);
return error;
}
return 0;
}
static int raydium_i2c_fw_write_page(struct i2c_client *client,
u16 page_idx, const void *data, size_t len)
{
u8 buf[RM_BL_WRT_LEN];
size_t xfer_len;
int error;
int i;
BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0);
for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) {
buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
buf[BL_PKG_IDX] = i + 1;
xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
memcpy(&buf[BL_DATA_STR], data, xfer_len);
if (len < RM_BL_WRT_PKG_SIZE)
memset(&buf[BL_DATA_STR + xfer_len], 0xff,
RM_BL_WRT_PKG_SIZE - xfer_len);
error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
RAYDIUM_WAIT_READY);
if (error) {
dev_err(&client->dev,
"page write command failed for page %d, chunk %d: %d\n",
page_idx, i, error);
return error;
}
data += xfer_len;
len -= xfer_len;
}
return error;
}
static u16 raydium_calc_chksum(const u8 *buf, u16 len)
{
u16 checksum = 0;
u16 i;
for (i = 0; i < len; i++)
checksum += buf[i];
return checksum;
}
static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
const struct firmware *fw)
{
struct i2c_client *client = ts->client;
const void *data;
size_t data_len;
size_t len;
int page_nr;
int i;
int error;
u16 fw_checksum;
if (fw->size == 0 || fw->size > RM_MAX_FW_SIZE) {
dev_err(&client->dev, "Invalid firmware length\n");
return -EINVAL;
}
error = raydium_i2c_check_fw_status(ts);
if (error) {
dev_err(&client->dev, "Unable to access IC %d\n", error);
return error;
}
if (ts->boot_mode == RAYDIUM_TS_MAIN) {
for (i = 0; i < RM_MAX_RETRIES; i++) {
error = raydium_i2c_enter_bl(client);
if (!error) {
error = raydium_i2c_check_fw_status(ts);
if (error) {
dev_err(&client->dev,
"unable to access IC: %d\n",
error);
return error;
}
if (ts->boot_mode == RAYDIUM_TS_BLDR)
break;
}
}
if (ts->boot_mode == RAYDIUM_TS_MAIN) {
dev_err(&client->dev,
"failied to jump to boot loader: %d\n",
error);
return -EIO;
}
}
error = raydium_i2c_disable_watch_dog(client);
if (error)
return error;
error = raydium_i2c_check_path(client);
if (error)
return error;
error = raydium_i2c_boot_trigger(client);
if (error) {
dev_err(&client->dev, "send boot trigger fail: %d\n", error);
return error;
}
msleep(RM_BOOT_DELAY_MS);
data = fw->data;
data_len = fw->size;
page_nr = 0;
while (data_len) {
len = min_t(size_t, data_len, RM_FW_PAGE_SIZE);
error = raydium_i2c_fw_write_page(client, page_nr++, data, len);
if (error)
return error;
msleep(20);
data += len;
data_len -= len;
}
error = raydium_i2c_leave_bl(client);
if (error) {
dev_err(&client->dev,
"failed to leave boot loader: %d\n", error);
return error;
}
dev_dbg(&client->dev, "left boot loader mode\n");
msleep(RM_BOOT_DELAY_MS);
error = raydium_i2c_check_fw_status(ts);
if (error) {
dev_err(&client->dev,
"failed to check fw status after write: %d\n",
error);
return error;
}
if (ts->boot_mode != RAYDIUM_TS_MAIN) {
dev_err(&client->dev,
"failed to switch to main fw after writing firmware: %d\n",
error);
return -EINVAL;
}
error = raydium_i2c_fw_trigger(client);
if (error) {
dev_err(&client->dev, "failed to trigger fw: %d\n", error);
return error;
}
fw_checksum = raydium_calc_chksum(fw->data, fw->size);
error = raydium_i2c_write_checksum(client, fw->size, fw_checksum);
if (error)
return error;
return 0;
}
static int raydium_i2c_fw_update(struct raydium_data *ts)
{
struct i2c_client *client = ts->client;
const struct firmware *fw = NULL;
const char *fw_file = "raydium.fw";
int error;
error = request_firmware(&fw, fw_file, &client->dev);
if (error) {
dev_err(&client->dev, "Unable to open firmware %s\n", fw_file);
return error;
}
disable_irq(client->irq);
error = raydium_i2c_do_update_firmware(ts, fw);
if (error) {
dev_err(&client->dev, "firmware update failed: %d\n", error);
ts->boot_mode = RAYDIUM_TS_BLDR;
goto out_enable_irq;
}
error = raydium_i2c_initialize(ts);
if (error) {
dev_err(&client->dev,
"failed to initialize device after firmware update: %d\n",
error);
ts->boot_mode = RAYDIUM_TS_BLDR;
goto out_enable_irq;
}
ts->boot_mode = RAYDIUM_TS_MAIN;
out_enable_irq:
enable_irq(client->irq);
msleep(100);
release_firmware(fw);
return error;
}
static void raydium_mt_event(struct raydium_data *ts)
{
int i;
for (i = 0; i < ts->report_size / ts->contact_size; i++) {
u8 *contact = &ts->report_data[ts->contact_size * i];
bool state = contact[RM_CONTACT_STATE_POS];
u8 wx, wy;
input_mt_slot(ts->input, i);
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
if (!state)
continue;
input_report_abs(ts->input, ABS_MT_POSITION_X,
get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
input_report_abs(ts->input, ABS_MT_POSITION_Y,
get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
input_report_abs(ts->input, ABS_MT_PRESSURE,
contact[RM_CONTACT_PRESSURE_POS]);
wx = contact[RM_CONTACT_WIDTH_X_POS];
wy = contact[RM_CONTACT_WIDTH_Y_POS];
input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
}
input_mt_sync_frame(ts->input);
input_sync(ts->input);
}
static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
{
struct raydium_data *ts = _dev;
int error;
u16 fw_crc;
u16 calc_crc;
if (ts->boot_mode != RAYDIUM_TS_MAIN)
goto out;
error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
ts->report_data, ts->pkg_size);
if (error)
goto out;
fw_crc = get_unaligned_le16(&ts->report_data[ts->report_size]);
calc_crc = raydium_calc_chksum(ts->report_data, ts->report_size);
if (unlikely(fw_crc != calc_crc)) {
dev_warn(&ts->client->dev,
"%s: invalid packet crc %#04x vs %#04x\n",
__func__, calc_crc, fw_crc);
goto out;
}
raydium_mt_event(ts);
out:
return IRQ_HANDLED;
}
static ssize_t raydium_i2c_fw_ver_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct raydium_data *ts = i2c_get_clientdata(client);
return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
}
static ssize_t raydium_i2c_hw_ver_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct raydium_data *ts = i2c_get_clientdata(client);
return sprintf(buf, "%#04x\n", le32_to_cpu(ts->info.hw_ver));
}
static ssize_t raydium_i2c_boot_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct raydium_data *ts = i2c_get_clientdata(client);
return sprintf(buf, "%s\n",
ts->boot_mode == RAYDIUM_TS_MAIN ?
"Normal" : "Recovery");
}
static ssize_t raydium_i2c_update_fw_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct raydium_data *ts = i2c_get_clientdata(client);
int error;
error = mutex_lock_interruptible(&ts->sysfs_mutex);
if (error)
return error;
error = raydium_i2c_fw_update(ts);
mutex_unlock(&ts->sysfs_mutex);
return error ?: count;
}
static ssize_t raydium_i2c_calibrate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct raydium_data *ts = i2c_get_clientdata(client);
static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E };
int error;
error = mutex_lock_interruptible(&ts->sysfs_mutex);
if (error)
return error;
error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
RAYDIUM_WAIT_READY);
if (error)
dev_err(&client->dev, "calibrate command failed: %d\n", error);
mutex_unlock(&ts->sysfs_mutex);
return error ?: count;
}
static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL);
static DEVICE_ATTR(hw_version, S_IRUGO, raydium_i2c_hw_ver_show, NULL);
static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_i2c_boot_mode_show, NULL);
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, raydium_i2c_update_fw_store);
static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_i2c_calibrate_store);
static struct attribute *raydium_i2c_attributes[] = {
&dev_attr_update_fw.attr,
&dev_attr_boot_mode.attr,
&dev_attr_fw_version.attr,
&dev_attr_hw_version.attr,
&dev_attr_calibrate.attr,
NULL
};
static struct attribute_group raydium_i2c_attribute_group = {
.attrs = raydium_i2c_attributes,
};
static void raydium_i2c_remove_sysfs_group(void *_data)
{
struct raydium_data *ts = _data;
sysfs_remove_group(&ts->client->dev.kobj, &raydium_i2c_attribute_group);
}
static int raydium_i2c_power_on(struct raydium_data *ts)
{
int error;
if (!ts->reset_gpio)
return 0;
gpiod_set_value_cansleep(ts->reset_gpio, 1);
error = regulator_enable(ts->avdd);
if (error) {
dev_err(&ts->client->dev,
"failed to enable avdd regulator: %d\n", error);
goto release_reset_gpio;
}
error = regulator_enable(ts->vccio);
if (error) {
regulator_disable(ts->avdd);
dev_err(&ts->client->dev,
"failed to enable vccio regulator: %d\n", error);
goto release_reset_gpio;
}
udelay(RM_POWERON_DELAY_USEC);
release_reset_gpio:
gpiod_set_value_cansleep(ts->reset_gpio, 0);
if (error)
return error;
msleep(RM_RESET_DELAY_MSEC);
return 0;
}
static void raydium_i2c_power_off(void *_data)
{
struct raydium_data *ts = _data;
if (ts->reset_gpio) {
gpiod_set_value_cansleep(ts->reset_gpio, 1);
regulator_disable(ts->vccio);
regulator_disable(ts->avdd);
}
}
static int raydium_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
union i2c_smbus_data dummy;
struct raydium_data *ts;
int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev,
"i2c check functionality error (need I2C_FUNC_I2C)\n");
return -ENXIO;
}
ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
mutex_init(&ts->sysfs_mutex);
ts->client = client;
i2c_set_clientdata(client, ts);
ts->avdd = devm_regulator_get(&client->dev, "avdd");
if (IS_ERR(ts->avdd)) {
error = PTR_ERR(ts->avdd);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"Failed to get 'avdd' regulator: %d\n", error);
return error;
}
ts->vccio = devm_regulator_get(&client->dev, "vccio");
if (IS_ERR(ts->vccio)) {
error = PTR_ERR(ts->vccio);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"Failed to get 'vccio' regulator: %d\n", error);
return error;
}
ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(ts->reset_gpio)) {
error = PTR_ERR(ts->reset_gpio);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"failed to get reset gpio: %d\n", error);
return error;
}
error = raydium_i2c_power_on(ts);
if (error)
return error;
error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
if (error) {
dev_err(&client->dev,
"failed to install power off action: %d\n", error);
raydium_i2c_power_off(ts);
return error;
}
/* Make sure there is something at this address */
if (i2c_smbus_xfer(client->adapter, client->addr, 0,
I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
dev_err(&client->dev, "nothing at this address\n");
return -ENXIO;
}
error = raydium_i2c_initialize(ts);
if (error) {
dev_err(&client->dev, "failed to initialize: %d\n", error);
return error;
}
ts->report_data = devm_kmalloc(&client->dev,
ts->pkg_size, GFP_KERNEL);
if (!ts->report_data)
return -ENOMEM;
ts->input = devm_input_allocate_device(&client->dev);
if (!ts->input) {
dev_err(&client->dev, "Failed to allocate input device\n");
return -ENOMEM;
}
ts->input->name = "Raydium Touchscreen";
ts->input->id.bustype = BUS_I2C;
input_set_drvdata(ts->input, ts);
input_set_abs_params(ts->input, ABS_MT_POSITION_X,
0, le16_to_cpu(ts->info.x_max), 0, 0);
input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
0, le16_to_cpu(ts->info.y_max), 0, 0);
input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
error = input_mt_init_slots(ts->input, RM_MAX_TOUCH_NUM,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(&client->dev,
"failed to initialize MT slots: %d\n", error);
return error;
}
error = input_register_device(ts->input);
if (error) {
dev_err(&client->dev,
"unable to register input device: %d\n", error);
return error;
}
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, raydium_i2c_irq,
IRQF_ONESHOT, client->name, ts);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
return error;
}
error = sysfs_create_group(&client->dev.kobj,
&raydium_i2c_attribute_group);
if (error) {
dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
error);
return error;
}
error = devm_add_action(&client->dev,
raydium_i2c_remove_sysfs_group, ts);
if (error) {
raydium_i2c_remove_sysfs_group(ts);
dev_err(&client->dev,
"Failed to add sysfs cleanup action: %d\n", error);
return error;
}
return 0;
}
static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
{
static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
int error;
error = raydium_i2c_send(client, RM_CMD_ENTER_SLEEP,
sleep_cmd, sizeof(sleep_cmd));
if (error)
dev_err(&client->dev,
"sleep command failed: %d\n", error);
}
static int __maybe_unused raydium_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct raydium_data *ts = i2c_get_clientdata(client);
/* Sleep is not available in BLDR recovery mode */
if (ts->boot_mode != RAYDIUM_TS_MAIN)
return -EBUSY;
disable_irq(client->irq);
if (device_may_wakeup(dev)) {
raydium_enter_sleep(client);
ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
} else {
raydium_i2c_power_off(ts);
}
return 0;
}
static int __maybe_unused raydium_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct raydium_data *ts = i2c_get_clientdata(client);
if (device_may_wakeup(dev)) {
if (ts->wake_irq_enabled)
disable_irq_wake(client->irq);
raydium_i2c_sw_reset(client);
} else {
raydium_i2c_power_on(ts);
raydium_i2c_initialize(ts);
}
enable_irq(client->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
raydium_i2c_suspend, raydium_i2c_resume);
static const struct i2c_device_id raydium_i2c_id[] = {
{ "raydium_i2c" , 0 },
{ "rm32380", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id raydium_acpi_id[] = {
{ "RAYD0001", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
#endif
#ifdef CONFIG_OF
static const struct of_device_id raydium_of_match[] = {
{ .compatible = "raydium,rm32380", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, raydium_of_match);
#endif
static struct i2c_driver raydium_i2c_driver = {
.probe = raydium_i2c_probe,
.id_table = raydium_i2c_id,
.driver = {
.name = "raydium_ts",
.pm = &raydium_i2c_pm_ops,
.acpi_match_table = ACPI_PTR(raydium_acpi_id),
.of_match_table = of_match_ptr(raydium_of_match),
},
};
module_i2c_driver(raydium_i2c_driver);
MODULE_AUTHOR("Raydium");
MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
MODULE_LICENSE("GPL v2");
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment