summaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-27 23:30:41 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-27 23:30:41 +0200
commitd85486d47123961bd8b08e94f6d4886c59a1fd76 (patch)
tree8bfcebed0fd1753fa8f0fa85286551b80e8ed7d1 /drivers/input/touchscreen
parentMerge branch 'i2c/for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/w... (diff)
parentMerge branch 'next' into for-linus (diff)
downloadlinux-d85486d47123961bd8b08e94f6d4886c59a1fd76.tar.xz
linux-d85486d47123961bd8b08e94f6d4886c59a1fd76.zip
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" ...
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig28
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/ad7879.c2
-rw-r--r--drivers/input/touchscreen/chipone_icn8318.c61
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c2
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c7
-rw-r--r--drivers/input/touchscreen/migor_ts.c6
-rw-r--r--drivers/input/touchscreen/of_touchscreen.c81
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c53
-rw-r--r--drivers/input/touchscreen/raydium_i2c_ts.c1238
-rw-r--r--drivers/input/touchscreen/surface3_spi.c427
-rw-r--r--drivers/input/touchscreen/ti_am335x_tsc.c2
-rw-r--r--drivers/input/touchscreen/tsc200x-core.c2
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c10
14 files changed, 1828 insertions, 93 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 8ecdc38fd489..ee02dc7422bd 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -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
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index f42975e719e0..3315882905f7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -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
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index e4bf1103e6f8..e16a44667da7 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -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);
diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c
index 22a6fead8cfb..0bf14067c167 100644
--- a/drivers/input/touchscreen/chipone_icn8318.c
+++ b/drivers/input/touchscreen/chipone_icn8318.c
@@ -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,
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
index 91cda8f8119d..79381cc1774a 100644
--- a/drivers/input/touchscreen/cyttsp_core.c
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -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) {
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 23fbe382da8b..703e295a37ed 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -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);
diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c
index c038db93e2c3..02fb11985819 100644
--- a/drivers/input/touchscreen/migor_ts.c
+++ b/drivers/input/touchscreen/migor_ts.c
@@ -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 = {
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c
index bb6f2fe14667..8d7f9c8f2771 100644
--- a/drivers/input/touchscreen/of_touchscreen.c
+++ b/drivers/input/touchscreen/of_touchscreen.c
@@ -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);
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 09523a3d3f23..d159e14f4d20 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -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");
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 000000000000..a99fb5cac5a0
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,1238 @@
+/*
+ * 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");
diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c
new file mode 100644
index 000000000000..e12fb9b63f31
--- /dev/null
+++ b/drivers/input/touchscreen/surface3_spi.c
@@ -0,0 +1,427 @@
+/*
+ * Driver for Ntrig/Microsoft Touchscreens over SPI
+ *
+ * Copyright (c) 2016 Red Hat Inc.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+
+#include <asm/unaligned.h>
+
+#define SURFACE3_PACKET_SIZE 264
+
+#define SURFACE3_REPORT_TOUCH 0xd2
+#define SURFACE3_REPORT_PEN 0x16
+
+struct surface3_ts_data {
+ struct spi_device *spi;
+ struct gpio_desc *gpiod_rst[2];
+ struct input_dev *input_dev;
+ struct input_dev *pen_input_dev;
+ int pen_tool;
+
+ u8 rd_buf[SURFACE3_PACKET_SIZE] ____cacheline_aligned;
+};
+
+struct surface3_ts_data_finger {
+ u8 status;
+ __le16 tracking_id;
+ __le16 x;
+ __le16 cx;
+ __le16 y;
+ __le16 cy;
+ __le16 width;
+ __le16 height;
+ u32 padding;
+} __packed;
+
+struct surface3_ts_data_pen {
+ u8 status;
+ __le16 x;
+ __le16 y;
+ __le16 pressure;
+ u8 padding;
+} __packed;
+
+static int surface3_spi_read(struct surface3_ts_data *ts_data)
+{
+ struct spi_device *spi = ts_data->spi;
+
+ memset(ts_data->rd_buf, 0, sizeof(ts_data->rd_buf));
+ return spi_read(spi, ts_data->rd_buf, sizeof(ts_data->rd_buf));
+}
+
+static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,
+ struct surface3_ts_data_finger *finger)
+{
+ int st = finger->status & 0x01;
+ int slot;
+
+ slot = input_mt_get_slot_by_key(ts_data->input_dev,
+ get_unaligned_le16(&finger->tracking_id));
+ if (slot < 0)
+ return;
+
+ input_mt_slot(ts_data->input_dev, slot);
+ input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, st);
+ if (st) {
+ input_report_abs(ts_data->input_dev,
+ ABS_MT_POSITION_X,
+ get_unaligned_le16(&finger->x));
+ input_report_abs(ts_data->input_dev,
+ ABS_MT_POSITION_Y,
+ get_unaligned_le16(&finger->y));
+ input_report_abs(ts_data->input_dev,
+ ABS_MT_WIDTH_MAJOR,
+ get_unaligned_le16(&finger->width));
+ input_report_abs(ts_data->input_dev,
+ ABS_MT_WIDTH_MINOR,
+ get_unaligned_le16(&finger->height));
+ }
+}
+
+static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data)
+{
+ u16 timestamp;
+ unsigned int i;
+ timestamp = get_unaligned_le16(&data[15]);
+
+ for (i = 0; i < 13; i++) {
+ struct surface3_ts_data_finger *finger;
+
+ finger = (struct surface3_ts_data_finger *)&data[17 +
+ i * sizeof(struct surface3_ts_data_finger)];
+
+ /*
+ * When bit 5 of status is 1, it marks the end of the report:
+ * - touch present: 0xe7
+ * - touch released: 0xe4
+ * - nothing valuable: 0xff
+ */
+ if (finger->status & 0x10)
+ break;
+
+ surface3_spi_report_touch(ts_data, finger);
+ }
+
+ input_mt_sync_frame(ts_data->input_dev);
+ input_sync(ts_data->input_dev);
+}
+
+static void surface3_spi_report_pen(struct surface3_ts_data *ts_data,
+ struct surface3_ts_data_pen *pen)
+{
+ struct input_dev *dev = ts_data->pen_input_dev;
+ int st = pen->status;
+ int prox = st & 0x01;
+ int rubber = st & 0x18;
+ int tool = (prox && rubber) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+ /* fake proximity out to switch tools */
+ if (ts_data->pen_tool != tool) {
+ input_report_key(dev, ts_data->pen_tool, 0);
+ input_sync(dev);
+ ts_data->pen_tool = tool;
+ }
+
+ input_report_key(dev, BTN_TOUCH, st & 0x12);
+
+ input_report_key(dev, ts_data->pen_tool, prox);
+
+ if (st) {
+ input_report_key(dev,
+ BTN_STYLUS,
+ st & 0x04);
+
+ input_report_abs(dev,
+ ABS_X,
+ get_unaligned_le16(&pen->x));
+ input_report_abs(dev,
+ ABS_Y,
+ get_unaligned_le16(&pen->y));
+ input_report_abs(dev,
+ ABS_PRESSURE,
+ get_unaligned_le16(&pen->pressure));
+ }
+}
+
+static void surface3_spi_process_pen(struct surface3_ts_data *ts_data, u8 *data)
+{
+ struct surface3_ts_data_pen *pen;
+
+ pen = (struct surface3_ts_data_pen *)&data[15];
+
+ surface3_spi_report_pen(ts_data, pen);
+ input_sync(ts_data->pen_input_dev);
+}
+
+static void surface3_spi_process(struct surface3_ts_data *ts_data)
+{
+ const char header[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xa5, 0x5a, 0xe7, 0x7e, 0x01
+ };
+ u8 *data = ts_data->rd_buf;
+
+ if (memcmp(header, data, sizeof(header)))
+ dev_err(&ts_data->spi->dev,
+ "%s header error: %*ph, ignoring...\n",
+ __func__, (int)sizeof(header), data);
+
+ switch (data[9]) {
+ case SURFACE3_REPORT_TOUCH:
+ surface3_spi_process_touch(ts_data, data);
+ break;
+ case SURFACE3_REPORT_PEN:
+ surface3_spi_process_pen(ts_data, data);
+ break;
+ default:
+ dev_err(&ts_data->spi->dev,
+ "%s unknown packet type: %x, ignoring...\n",
+ __func__, data[9]);
+ break;
+ }
+}
+
+static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id)
+{
+ struct surface3_ts_data *data = dev_id;
+
+ if (surface3_spi_read(data))
+ return IRQ_HANDLED;
+
+ dev_dbg(&data->spi->dev, "%s received -> %*ph\n",
+ __func__, SURFACE3_PACKET_SIZE, data->rd_buf);
+ surface3_spi_process(data);
+
+ return IRQ_HANDLED;
+}
+
+static void surface3_spi_power(struct surface3_ts_data *data, bool on)
+{
+ gpiod_set_value(data->gpiod_rst[0], on);
+ gpiod_set_value(data->gpiod_rst[1], on);
+ /* let the device settle a little */
+ msleep(20);
+}
+
+/**
+ * surface3_spi_get_gpio_config - Get GPIO config from ACPI/DT
+ *
+ * @ts: surface3_spi_ts_data pointer
+ */
+static int surface3_spi_get_gpio_config(struct surface3_ts_data *data)
+{
+ int error;
+ struct device *dev;
+ struct gpio_desc *gpiod;
+ int i;
+
+ dev = &data->spi->dev;
+
+ /* Get the reset lines GPIO pin number */
+ for (i = 0; i < 2; i++) {
+ gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod)) {
+ error = PTR_ERR(gpiod);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev,
+ "Failed to get power GPIO %d: %d\n",
+ i,
+ error);
+ return error;
+ }
+
+ data->gpiod_rst[i] = gpiod;
+ }
+
+ return 0;
+}
+
+static int surface3_spi_create_touch_input(struct surface3_ts_data *data)
+{
+ struct input_dev *input;
+ int error;
+
+ input = devm_input_allocate_device(&data->spi->dev);
+ if (!input)
+ return -ENOMEM;
+
+ data->input_dev = input;
+
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, 9600, 0, 0);
+ input_abs_set_res(input, ABS_MT_POSITION_X, 40);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 7200, 0, 0);
+ input_abs_set_res(input, ABS_MT_POSITION_Y, 48);
+ input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 1024, 0, 0);
+ input_mt_init_slots(input, 10, INPUT_MT_DIRECT);
+
+ input->name = "Surface3 SPI Capacitive TouchScreen";
+ input->phys = "input/ts";
+ input->id.bustype = BUS_SPI;
+ input->id.vendor = 0x045e; /* Microsoft */
+ input->id.product = 0x0001;
+ input->id.version = 0x0000;
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&data->spi->dev,
+ "Failed to register input device: %d", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int surface3_spi_create_pen_input(struct surface3_ts_data *data)
+{
+ struct input_dev *input;
+ int error;
+
+ input = devm_input_allocate_device(&data->spi->dev);
+ if (!input)
+ return -ENOMEM;
+
+ data->pen_input_dev = input;
+ data->pen_tool = BTN_TOOL_PEN;
+
+ __set_bit(INPUT_PROP_DIRECT, input->propbit);
+ __set_bit(INPUT_PROP_POINTER, input->propbit);
+ input_set_abs_params(input, ABS_X, 0, 9600, 0, 0);
+ input_abs_set_res(input, ABS_X, 40);
+ input_set_abs_params(input, ABS_Y, 0, 7200, 0, 0);
+ input_abs_set_res(input, ABS_Y, 48);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 1024, 0, 0);
+ input_set_capability(input, EV_KEY, BTN_TOUCH);
+ input_set_capability(input, EV_KEY, BTN_STYLUS);
+ input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
+ input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
+
+ input->name = "Surface3 SPI Pen Input";
+ input->phys = "input/ts";
+ input->id.bustype = BUS_SPI;
+ input->id.vendor = 0x045e; /* Microsoft */
+ input->id.product = 0x0002;
+ input->id.version = 0x0000;
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&data->spi->dev,
+ "Failed to register input device: %d", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int surface3_spi_probe(struct spi_device *spi)
+{
+ struct surface3_ts_data *data;
+ int error;
+
+ /* Set up SPI*/
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ error = spi_setup(spi);
+ if (error)
+ return error;
+
+ data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->spi = spi;
+ spi_set_drvdata(spi, data);
+
+ error = surface3_spi_get_gpio_config(data);
+ if (error)
+ return error;
+
+ surface3_spi_power(data, true);
+ surface3_spi_power(data, false);
+ surface3_spi_power(data, true);
+
+ error = surface3_spi_create_touch_input(data);
+ if (error)
+ return error;
+
+ error = surface3_spi_create_pen_input(data);
+ if (error)
+ return error;
+
+ error = devm_request_threaded_irq(&spi->dev, spi->irq,
+ NULL, surface3_spi_irq_handler,
+ IRQF_ONESHOT,
+ "Surface3-irq", data);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int __maybe_unused surface3_spi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct surface3_ts_data *data = spi_get_drvdata(spi);
+
+ disable_irq(data->spi->irq);
+
+ surface3_spi_power(data, false);
+
+ return 0;
+}
+
+static int __maybe_unused surface3_spi_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct surface3_ts_data *data = spi_get_drvdata(spi);
+
+ surface3_spi_power(data, true);
+
+ enable_irq(data->spi->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(surface3_spi_pm_ops,
+ surface3_spi_suspend,
+ surface3_spi_resume);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id surface3_spi_acpi_match[] = {
+ { "MSHW0037", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, surface3_spi_acpi_match);
+#endif
+
+static struct spi_driver surface3_spi_driver = {
+ .driver = {
+ .name = "Surface3-spi",
+ .acpi_match_table = ACPI_PTR(surface3_spi_acpi_match),
+ .pm = &surface3_spi_pm_ops,
+ },
+ .probe = surface3_spi_probe,
+};
+
+module_spi_driver(surface3_spi_driver);
+
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_DESCRIPTION("Surface 3 SPI touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 8b3f15ca7725..7953381d939a 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -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");
diff --git a/drivers/input/touchscreen/tsc200x-core.c b/drivers/input/touchscreen/tsc200x-core.c
index dfa7f1c4f545..b7059ed8872e 100644
--- a/drivers/input/touchscreen/tsc200x-core.c
+++ b/drivers/input/touchscreen/tsc200x-core.c
@@ -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;
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index b6fc4bde79de..85e95725d0df 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -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)