summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/input/input.c34
-rw-r--r--drivers/input/joystick/xpad.c78
-rw-r--r--drivers/input/keyboard/imx_keypad.c4
-rw-r--r--drivers/input/misc/axp20x-pek.c8
-rw-r--r--drivers/input/serio/Kconfig1
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c9
-rw-r--r--drivers/input/touchscreen/of_touchscreen.c69
-rw-r--r--drivers/input/touchscreen/tsc2005.c2
-rw-r--r--drivers/input/touchscreen/wdt87xx_i2c.c1149
-rw-r--r--include/linux/input/touchscreen.h5
12 files changed, 1296 insertions, 76 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c
index f31578423636..78d24990a816 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -677,12 +677,9 @@ static void input_dev_release_keys(struct input_dev *dev)
int code;
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
- for (code = 0; code <= KEY_MAX; code++) {
- if (is_event_supported(code, dev->keybit, KEY_MAX) &&
- __test_and_clear_bit(code, dev->key)) {
- input_pass_event(dev, EV_KEY, code, 0);
- }
- }
+ for_each_set_bit(code, dev->key, KEY_CNT)
+ input_pass_event(dev, EV_KEY, code, 0);
+ memset(dev->key, 0, sizeof(dev->key));
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
}
}
@@ -1626,10 +1623,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
if (!test_bit(EV_##type, dev->evbit)) \
break; \
\
- for (i = 0; i < type##_MAX; i++) { \
- if (!test_bit(i, dev->bits##bit)) \
- continue; \
- \
+ for_each_set_bit(i, dev->bits##bit, type##_CNT) { \
active = test_bit(i, dev->bits); \
if (!active && !on) \
continue; \
@@ -1980,22 +1974,12 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */
- if (test_bit(EV_ABS, dev->evbit)) {
- for (i = 0; i < ABS_CNT; i++) {
- if (test_bit(i, dev->absbit)) {
- if (input_is_mt_axis(i))
- events += mt_slots;
- else
- events++;
- }
- }
- }
+ if (test_bit(EV_ABS, dev->evbit))
+ for_each_set_bit(i, dev->absbit, ABS_CNT)
+ events += input_is_mt_axis(i) ? mt_slots : 1;
- if (test_bit(EV_REL, dev->evbit)) {
- for (i = 0; i < REL_CNT; i++)
- if (test_bit(i, dev->relbit))
- events++;
- }
+ if (test_bit(EV_REL, dev->evbit))
+ events += bitmap_weight(dev->relbit, REL_CNT);
/* Make room for KEY and MSC events */
events += 7;
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 61c761156371..f8850f9cb331 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -344,6 +344,7 @@ struct usb_xpad {
int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */
+ unsigned long led_no; /* led to lit on xbox360 controllers */
};
/*
@@ -488,6 +489,8 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
input_sync(dev);
}
+static void xpad_identify_controller(struct usb_xpad *xpad);
+
/*
* xpad360w_process_packet
*
@@ -510,6 +513,11 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
if (data[1] & 0x80) {
xpad->pad_present = 1;
usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
+ /*
+ * Light up the segment corresponding to
+ * controller number.
+ */
+ xpad_identify_controller(xpad);
} else
xpad->pad_present = 0;
}
@@ -881,17 +889,63 @@ struct xpad_led {
struct usb_xpad *xpad;
};
+/**
+ * @param command
+ * 0: off
+ * 1: all blink, then previous setting
+ * 2: 1/top-left blink, then on
+ * 3: 2/top-right blink, then on
+ * 4: 3/bottom-left blink, then on
+ * 5: 4/bottom-right blink, then on
+ * 6: 1/top-left on
+ * 7: 2/top-right on
+ * 8: 3/bottom-left on
+ * 9: 4/bottom-right on
+ * 10: rotate
+ * 11: blink, based on previous setting
+ * 12: slow blink, based on previous setting
+ * 13: rotate with two lights
+ * 14: persistent slow all blink
+ * 15: blink once, then previous setting
+ */
static void xpad_send_led_command(struct usb_xpad *xpad, int command)
{
- if (command >= 0 && command < 14) {
- mutex_lock(&xpad->odata_mutex);
+ command %= 16;
+
+ mutex_lock(&xpad->odata_mutex);
+
+ switch (xpad->xtype) {
+ case XTYPE_XBOX360:
xpad->odata[0] = 0x01;
xpad->odata[1] = 0x03;
xpad->odata[2] = command;
xpad->irq_out->transfer_buffer_length = 3;
- usb_submit_urb(xpad->irq_out, GFP_KERNEL);
- mutex_unlock(&xpad->odata_mutex);
+ break;
+ case XTYPE_XBOX360W:
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x00;
+ xpad->odata[2] = 0x08;
+ xpad->odata[3] = 0x40 + command;
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+ break;
}
+
+ usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+ mutex_unlock(&xpad->odata_mutex);
+}
+
+static void xpad_identify_controller(struct usb_xpad *xpad)
+{
+ /* Light up the segment corresponding to controller number */
+ xpad_send_led_command(xpad, (xpad->led_no % 4) + 2);
}
static void xpad_led_set(struct led_classdev *led_cdev,
@@ -905,22 +959,21 @@ static void xpad_led_set(struct led_classdev *led_cdev,
static int xpad_led_probe(struct usb_xpad *xpad)
{
- static atomic_t led_seq = ATOMIC_INIT(-1);
- unsigned long led_no;
+ static atomic_t led_seq = ATOMIC_INIT(-1);
struct xpad_led *led;
struct led_classdev *led_cdev;
int error;
- if (xpad->xtype != XTYPE_XBOX360)
+ if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W)
return 0;
xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL);
if (!led)
return -ENOMEM;
- led_no = atomic_inc_return(&led_seq);
+ xpad->led_no = atomic_inc_return(&led_seq);
- snprintf(led->name, sizeof(led->name), "xpad%lu", led_no);
+ snprintf(led->name, sizeof(led->name), "xpad%lu", xpad->led_no);
led->xpad = xpad;
led_cdev = &led->led_cdev;
@@ -934,10 +987,8 @@ static int xpad_led_probe(struct usb_xpad *xpad)
return error;
}
- /*
- * Light up the segment corresponding to controller number
- */
- xpad_send_led_command(xpad, (led_no % 4) + 2);
+ /* Light up the segment corresponding to controller number */
+ xpad_identify_controller(xpad);
return 0;
}
@@ -954,6 +1005,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad)
#else
static int xpad_led_probe(struct usb_xpad *xpad) { return 0; }
static void xpad_led_disconnect(struct usb_xpad *xpad) { }
+static void xpad_identify_controller(struct usb_xpad *xpad) { }
#endif
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index 2e855e6f3565..d2ea863d6a45 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -506,7 +506,9 @@ static int imx_keypad_probe(struct platform_device *pdev)
input_set_drvdata(input_dev, keypad);
/* Ensure that the keypad will stay dormant until opened */
- clk_prepare_enable(keypad->clk);
+ error = clk_prepare_enable(keypad->clk);
+ if (error)
+ return error;
imx_keypad_inhibit(keypad);
clk_disable_unprepare(keypad->clk);
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
index f1c844739cd7..10e140af5aac 100644
--- a/drivers/input/misc/axp20x-pek.c
+++ b/drivers/input/misc/axp20x-pek.c
@@ -167,9 +167,13 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
struct input_dev *idev = pwr;
struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
- if (irq == axp20x_pek->irq_dbr)
+ /*
+ * The power-button is connected to ground so a falling edge (dbf)
+ * means it is pressed.
+ */
+ if (irq == axp20x_pek->irq_dbf)
input_report_key(idev, KEY_POWER, true);
- else if (irq == axp20x_pek->irq_dbf)
+ else if (irq == axp20x_pek->irq_dbr)
input_report_key(idev, KEY_POWER, false);
input_sync(idev);
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 77833d7a004b..200841b77edb 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -244,6 +244,7 @@ config SERIO_PS2MULT
config SERIO_ARC_PS2
tristate "ARC PS/2 support"
+ depends on HAS_IOMEM
help
Say Y here if you have an ARC FPGA platform with a PS/2
controller in it.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index d20fe1dff403..a854c6e5f09e 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -658,6 +658,18 @@ config TOUCHSCREEN_PIXCIR
To compile this driver as a module, choose M here: the
module will be called pixcir_i2c_ts.
+config TOUCHSCREEN_WDT87XX_I2C
+ tristate "Weida HiTech I2C touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a Weida WDT87XX I2C touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wdt87xx_i2c.
+
config TOUCHSCREEN_WM831X
tristate "Support for WM831x touchscreen controllers"
depends on MFD_WM831X
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 44deea743d02..fa3d33bac7fc 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_WDT87XX_I2C) += wdt87xx_i2c.o
obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index e6aef3e48bd9..394b1de9a2a3 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -1035,20 +1035,15 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
- __set_bit(EV_KEY, input->evbit);
- __set_bit(EV_ABS, input->evbit);
- __set_bit(BTN_TOUCH, input->keybit);
- input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0);
- input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_X,
0, tsdata->num_x * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0);
if (!pdata)
- touchscreen_parse_of_params(input);
+ touchscreen_parse_of_params(input, true);
- error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
+ error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
return error;
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c
index b82b5207c78b..806cd0ad160f 100644
--- a/drivers/input/touchscreen/of_touchscreen.c
+++ b/drivers/input/touchscreen/of_touchscreen.c
@@ -14,14 +14,22 @@
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
-static u32 of_get_optional_u32(struct device_node *np,
- const char *property)
+static bool touchscreen_get_prop_u32(struct device_node *np,
+ const char *property,
+ unsigned int default_value,
+ unsigned int *value)
{
- u32 val = 0;
+ u32 val;
+ int error;
- of_property_read_u32(np, property, &val);
+ error = of_property_read_u32(np, property, &val);
+ if (error) {
+ *value = default_value;
+ return false;
+ }
- return val;
+ *value = val;
+ return true;
}
static void touchscreen_set_params(struct input_dev *dev,
@@ -54,34 +62,45 @@ static void touchscreen_set_params(struct input_dev *dev,
* input device accordingly. The function keeps previously setuped default
* values if no value is specified via DT.
*/
-void touchscreen_parse_of_params(struct input_dev *dev)
+void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch)
{
struct device_node *np = dev->dev.parent->of_node;
- u32 maximum, fuzz;
+ unsigned int axis;
+ unsigned int maximum, fuzz;
+ bool data_present;
input_alloc_absinfo(dev);
if (!dev->absinfo)
return;
- maximum = of_get_optional_u32(np, "touchscreen-size-x");
- fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x");
- if (maximum || fuzz) {
- touchscreen_set_params(dev, ABS_X, maximum, fuzz);
- touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz);
- }
+ axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
+ data_present = touchscreen_get_prop_u32(np, "touchscreen-size-x",
+ input_abs_get_max(dev, axis),
+ &maximum) |
+ touchscreen_get_prop_u32(np, "touchscreen-fuzz-x",
+ input_abs_get_fuzz(dev, axis),
+ &fuzz);
+ if (data_present)
+ touchscreen_set_params(dev, axis, maximum, fuzz);
- maximum = of_get_optional_u32(np, "touchscreen-size-y");
- fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y");
- if (maximum || fuzz) {
- touchscreen_set_params(dev, ABS_Y, maximum, fuzz);
- touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz);
- }
+ axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
+ data_present = touchscreen_get_prop_u32(np, "touchscreen-size-y",
+ input_abs_get_max(dev, axis),
+ &maximum) |
+ touchscreen_get_prop_u32(np, "touchscreen-fuzz-y",
+ input_abs_get_fuzz(dev, axis),
+ &fuzz);
+ if (data_present)
+ touchscreen_set_params(dev, axis, maximum, fuzz);
- maximum = of_get_optional_u32(np, "touchscreen-max-pressure");
- fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure");
- if (maximum || fuzz) {
- touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz);
- touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz);
- }
+ axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
+ data_present = touchscreen_get_prop_u32(np, "touchscreen-max-pressure",
+ input_abs_get_max(dev, axis),
+ &maximum) |
+ touchscreen_get_prop_u32(np, "touchscreen-fuzz-pressure",
+ input_abs_get_fuzz(dev, axis),
+ &fuzz);
+ if (data_present)
+ touchscreen_set_params(dev, axis, maximum, fuzz);
}
EXPORT_SYMBOL(touchscreen_parse_of_params);
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
index 72657c579430..d8c025b0f88c 100644
--- a/drivers/input/touchscreen/tsc2005.c
+++ b/drivers/input/touchscreen/tsc2005.c
@@ -709,7 +709,7 @@ static int tsc2005_probe(struct spi_device *spi)
input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
if (np)
- touchscreen_parse_of_params(input_dev);
+ touchscreen_parse_of_params(input_dev, false);
input_dev->open = tsc2005_open;
input_dev->close = tsc2005_close;
diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c
new file mode 100644
index 000000000000..fb92ae1c5fae
--- /dev/null
+++ b/drivers/input/touchscreen/wdt87xx_i2c.c
@@ -0,0 +1,1149 @@
+/*
+ * Weida HiTech WDT87xx TouchScreen I2C driver
+ *
+ * Copyright (c) 2015 Weida Hi-Tech Co., Ltd.
+ * HN Chen <hn.chen@weidahitech.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <asm/unaligned.h>
+
+#define WDT87XX_NAME "wdt87xx_i2c"
+#define WDT87XX_DRV_VER "0.9.6"
+#define WDT87XX_FW_NAME "wdt87xx_fw.bin"
+#define WDT87XX_CFG_NAME "wdt87xx_cfg.bin"
+
+#define MODE_ACTIVE 0x01
+#define MODE_READY 0x02
+#define MODE_IDLE 0x03
+#define MODE_SLEEP 0x04
+#define MODE_STOP 0xFF
+
+#define WDT_MAX_FINGER 10
+#define WDT_RAW_BUF_COUNT 54
+#define WDT_V1_RAW_BUF_COUNT 74
+#define WDT_FIRMWARE_ID 0xa9e368f5
+
+#define PG_SIZE 0x1000
+#define MAX_RETRIES 3
+
+#define MAX_UNIT_AXIS 0x7FFF
+
+#define PKT_READ_SIZE 72
+#define PKT_WRITE_SIZE 80
+
+/* the finger definition of the report event */
+#define FINGER_EV_OFFSET_ID 0
+#define FINGER_EV_OFFSET_X 1
+#define FINGER_EV_OFFSET_Y 3
+#define FINGER_EV_SIZE 5
+
+#define FINGER_EV_V1_OFFSET_ID 0
+#define FINGER_EV_V1_OFFSET_W 1
+#define FINGER_EV_V1_OFFSET_P 2
+#define FINGER_EV_V1_OFFSET_X 3
+#define FINGER_EV_V1_OFFSET_Y 5
+#define FINGER_EV_V1_SIZE 7
+
+/* The definition of a report packet */
+#define TOUCH_PK_OFFSET_REPORT_ID 0
+#define TOUCH_PK_OFFSET_EVENT 1
+#define TOUCH_PK_OFFSET_SCAN_TIME 51
+#define TOUCH_PK_OFFSET_FNGR_NUM 53
+
+#define TOUCH_PK_V1_OFFSET_REPORT_ID 0
+#define TOUCH_PK_V1_OFFSET_EVENT 1
+#define TOUCH_PK_V1_OFFSET_SCAN_TIME 71
+#define TOUCH_PK_V1_OFFSET_FNGR_NUM 73
+
+/* The definition of the controller parameters */
+#define CTL_PARAM_OFFSET_FW_ID 0
+#define CTL_PARAM_OFFSET_PLAT_ID 2
+#define CTL_PARAM_OFFSET_XMLS_ID1 4
+#define CTL_PARAM_OFFSET_XMLS_ID2 6
+#define CTL_PARAM_OFFSET_PHY_CH_X 8
+#define CTL_PARAM_OFFSET_PHY_CH_Y 10
+#define CTL_PARAM_OFFSET_PHY_X0 12
+#define CTL_PARAM_OFFSET_PHY_X1 14
+#define CTL_PARAM_OFFSET_PHY_Y0 16
+#define CTL_PARAM_OFFSET_PHY_Y1 18
+#define CTL_PARAM_OFFSET_PHY_W 22
+#define CTL_PARAM_OFFSET_PHY_H 24
+#define CTL_PARAM_OFFSET_FACTOR 32
+
+/* Communication commands */
+#define PACKET_SIZE 56
+#define VND_REQ_READ 0x06
+#define VND_READ_DATA 0x07
+#define VND_REQ_WRITE 0x08
+
+#define VND_CMD_START 0x00
+#define VND_CMD_STOP 0x01
+#define VND_CMD_RESET 0x09
+
+#define VND_CMD_ERASE 0x1A
+
+#define VND_GET_CHECKSUM 0x66
+
+#define VND_SET_DATA 0x83
+#define VND_SET_COMMAND_DATA 0x84
+#define VND_SET_CHECKSUM_CALC 0x86
+#define VND_SET_CHECKSUM_LENGTH 0x87
+
+#define VND_CMD_SFLCK 0xFC
+#define VND_CMD_SFUNL 0xFD
+
+#define CMD_SFLCK_KEY 0xC39B
+#define CMD_SFUNL_KEY 0x95DA
+
+#define STRIDX_PLATFORM_ID 0x80
+#define STRIDX_PARAMETERS 0x81
+
+#define CMD_BUF_SIZE 8
+#define PKT_BUF_SIZE 64
+
+/* The definition of the command packet */
+#define CMD_REPORT_ID_OFFSET 0x0
+#define CMD_TYPE_OFFSET 0x1
+#define CMD_INDEX_OFFSET 0x2
+#define CMD_KEY_OFFSET 0x3
+#define CMD_LENGTH_OFFSET 0x4
+#define CMD_DATA_OFFSET 0x8
+
+/* The definition of firmware chunk tags */
+#define FOURCC_ID_RIFF 0x46464952
+#define FOURCC_ID_WHIF 0x46494857
+#define FOURCC_ID_FRMT 0x544D5246
+#define FOURCC_ID_FRWR 0x52575246
+#define FOURCC_ID_CNFG 0x47464E43
+
+#define CHUNK_ID_FRMT FOURCC_ID_FRMT
+#define CHUNK_ID_FRWR FOURCC_ID_FRWR
+#define CHUNK_ID_CNFG FOURCC_ID_CNFG
+
+#define FW_FOURCC1_OFFSET 0
+#define FW_SIZE_OFFSET 4
+#define FW_FOURCC2_OFFSET 8
+#define FW_PAYLOAD_OFFSET 40
+
+#define FW_CHUNK_ID_OFFSET 0
+#define FW_CHUNK_SIZE_OFFSET 4
+#define FW_CHUNK_TGT_START_OFFSET 8
+#define FW_CHUNK_PAYLOAD_LEN_OFFSET 12
+#define FW_CHUNK_SRC_START_OFFSET 16
+#define FW_CHUNK_VERSION_OFFSET 20
+#define FW_CHUNK_ATTR_OFFSET 24
+#define FW_CHUNK_PAYLOAD_OFFSET 32
+
+/* Controller requires minimum 300us between commands */
+#define WDT_COMMAND_DELAY_MS 2
+#define WDT_FLASH_WRITE_DELAY_MS 4
+
+struct wdt87xx_sys_param {
+ u16 fw_id;
+ u16 plat_id;
+ u16 xmls_id1;
+ u16 xmls_id2;
+ u16 phy_ch_x;
+ u16 phy_ch_y;
+ u16 phy_w;
+ u16 phy_h;
+ u16 scaling_factor;
+ u32 max_x;
+ u32 max_y;
+};
+
+struct wdt87xx_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ /* Mutex for fw update to prevent concurrent access */
+ struct mutex fw_mutex;
+ struct wdt87xx_sys_param param;
+ u8 phys[32];
+};
+
+static int wdt87xx_i2c_xfer(struct i2c_client *client,
+ void *txdata, size_t txlen,
+ void *rxdata, size_t rxlen)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = txlen,
+ .buf = txdata,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = rxlen,
+ .buf = rxdata,
+ },
+ };
+ int error;
+ int ret;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev, "%s: i2c transfer failed: %d\n",
+ __func__, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int wdt87xx_get_string(struct i2c_client *client, u8 str_idx,
+ u8 *buf, size_t len)
+{
+ u8 tx_buf[] = { 0x22, 0x00, 0x13, 0x0E, str_idx, 0x23, 0x00 };
+ u8 rx_buf[PKT_WRITE_SIZE];
+ size_t rx_len = len + 2;
+ int error;
+
+ if (rx_len > sizeof(rx_buf))
+ return -EINVAL;
+
+ error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf),
+ rx_buf, rx_len);
+ if (error) {
+ dev_err(&client->dev, "get string failed: %d\n", error);
+ return error;
+ }
+
+ if (rx_buf[1] != 0x03) {
+ dev_err(&client->dev, "unexpected response to get string: %d\n",
+ rx_buf[1]);
+ return -EINVAL;
+ }
+
+ rx_len = min_t(size_t, len, rx_buf[0]);
+ memcpy(buf, &rx_buf[2], rx_len);
+
+ mdelay(WDT_COMMAND_DELAY_MS);
+
+ return 0;
+}
+
+static int wdt87xx_get_feature(struct i2c_client *client,
+ u8 *buf, size_t buf_size)
+{
+ u8 tx_buf[8];
+ u8 rx_buf[PKT_WRITE_SIZE];
+ size_t tx_len = 0;
+ size_t rx_len = buf_size + 2;
+ int error;
+
+ if (rx_len > sizeof(rx_buf))
+ return -EINVAL;
+
+ /* Get feature command packet */
+ tx_buf[tx_len++] = 0x22;
+ tx_buf[tx_len++] = 0x00;
+ if (buf[CMD_REPORT_ID_OFFSET] > 0xF) {
+ tx_buf[tx_len++] = 0x30;
+ tx_buf[tx_len++] = 0x02;
+ tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET];
+ } else {
+ tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET];
+ tx_buf[tx_len++] = 0x02;
+ }
+ tx_buf[tx_len++] = 0x23;
+ tx_buf[tx_len++] = 0x00;
+
+ error = wdt87xx_i2c_xfer(client, tx_buf, tx_len, rx_buf, rx_len);
+ if (error) {
+ dev_err(&client->dev, "get feature failed: %d\n", error);
+ return error;
+ }
+
+ rx_len = min_t(size_t, buf_size, get_unaligned_le16(rx_buf));
+ memcpy(buf, &rx_buf[2], rx_len);
+
+ mdelay(WDT_COMMAND_DELAY_MS);
+
+ return 0;
+}
+
+static int wdt87xx_set_feature(struct i2c_client *client,
+ const u8 *buf, size_t buf_size)
+{
+ u8 tx_buf[PKT_WRITE_SIZE];
+ int tx_len = 0;
+ int error;
+
+ /* Set feature command packet */
+ tx_buf[tx_len++] = 0x22;
+ tx_buf[tx_len++] = 0x00;
+ if (buf[CMD_REPORT_ID_OFFSET] > 0xF) {
+ tx_buf[tx_len++] = 0x30;
+ tx_buf[tx_len++] = 0x03;
+ tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET];
+ } else {
+ tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET];
+ tx_buf[tx_len++] = 0x03;
+ }
+ tx_buf[tx_len++] = 0x23;
+ tx_buf[tx_len++] = 0x00;
+ tx_buf[tx_len++] = (buf_size & 0xFF);
+ tx_buf[tx_len++] = ((buf_size & 0xFF00) >> 8);
+
+ if (tx_len + buf_size > sizeof(tx_buf))
+ return -EINVAL;
+
+ memcpy(&tx_buf[tx_len], buf, buf_size);
+ tx_len += buf_size;
+
+ error = i2c_master_send(client, tx_buf, tx_len);
+ if (error < 0) {
+ dev_err(&client->dev, "set feature failed: %d\n", error);
+ return error;
+ }
+
+ mdelay(WDT_COMMAND_DELAY_MS);
+
+ return 0;
+}
+
+static int wdt87xx_send_command(struct i2c_client *client, int cmd, int value)
+{
+ u8 cmd_buf[CMD_BUF_SIZE];
+
+ /* Set the command packet */
+ cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE;
+ cmd_buf[CMD_TYPE_OFFSET] = VND_SET_COMMAND_DATA;
+ put_unaligned_le16((u16)cmd, &cmd_buf[CMD_INDEX_OFFSET]);
+
+ switch (cmd) {
+ case VND_CMD_START:
+ case VND_CMD_STOP:
+ case VND_CMD_RESET:
+ /* Mode selector */
+ put_unaligned_le32((value & 0xFF), &cmd_buf[CMD_LENGTH_OFFSET]);
+ break;
+
+ case VND_CMD_SFLCK:
+ put_unaligned_le16(CMD_SFLCK_KEY, &cmd_buf[CMD_KEY_OFFSET]);
+ break;
+
+ case VND_CMD_SFUNL:
+ put_unaligned_le16(CMD_SFUNL_KEY, &cmd_buf[CMD_KEY_OFFSET]);
+ break;
+
+ case VND_CMD_ERASE:
+ case VND_SET_CHECKSUM_CALC:
+ case VND_SET_CHECKSUM_LENGTH:
+ put_unaligned_le32(value, &cmd_buf[CMD_KEY_OFFSET]);
+ break;
+
+ default:
+ cmd_buf[CMD_REPORT_ID_OFFSET] = 0;
+ dev_err(&client->dev, "Invalid command: %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf));
+}
+
+static int wdt87xx_sw_reset(struct i2c_client *client)
+{
+ int error;
+
+ dev_dbg(&client->dev, "resetting device now\n");
+
+ error = wdt87xx_send_command(client, VND_CMD_RESET, 0);
+ if (error) {
+ dev_err(&client->dev, "reset failed\n");
+ return error;
+ }
+
+ /* Wait the device to be ready */
+ msleep(200);
+
+ return 0;
+}
+
+static const void *wdt87xx_get_fw_chunk(const struct firmware *fw, u32 id)
+{
+ size_t pos = FW_PAYLOAD_OFFSET;
+ u32 chunk_id, chunk_size;
+
+ while (pos < fw->size) {
+ chunk_id = get_unaligned_le32(fw->data +
+ pos + FW_CHUNK_ID_OFFSET);
+ if (chunk_id == id)
+ return fw->data + pos;
+
+ chunk_size = get_unaligned_le32(fw->data +
+ pos + FW_CHUNK_SIZE_OFFSET);
+ pos += chunk_size + 2 * sizeof(u32); /* chunk ID + size */
+ }
+
+ return NULL;
+}
+
+static int wdt87xx_get_sysparam(struct i2c_client *client,
+ struct wdt87xx_sys_param *param)
+{
+ u8 buf[PKT_READ_SIZE];
+ int error;
+
+ error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34);
+ if (error) {
+ dev_err(&client->dev, "failed to get parameters\n");
+ return error;
+ }
+
+ param->xmls_id1 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID1);
+ param->xmls_id2 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID2);
+ param->phy_ch_x = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_X);
+ param->phy_ch_y = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_Y);
+ param->phy_w = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_W) / 10;
+ param->phy_h = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_H) / 10;
+
+ /* Get the scaling factor of pixel to logical coordinate */
+ param->scaling_factor =
+ get_unaligned_le16(buf + CTL_PARAM_OFFSET_FACTOR);
+
+ param->max_x = MAX_UNIT_AXIS;
+ param->max_y = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS * param->phy_h,
+ param->phy_w);
+
+ error = wdt87xx_get_string(client, STRIDX_PLATFORM_ID, buf, 8);
+ if (error) {
+ dev_err(&client->dev, "failed to get platform id\n");
+ return error;
+ }
+
+ param->plat_id = buf[1];
+
+ buf[0] = 0xf2;
+ error = wdt87xx_get_feature(client, buf, 16);
+ if (error) {
+ dev_err(&client->dev, "failed to get firmware id\n");
+ return error;
+ }
+
+ if (buf[0] != 0xf2) {
+ dev_err(&client->dev, "wrong id of fw response: 0x%x\n",
+ buf[0]);
+ return -EINVAL;
+ }
+
+ param->fw_id = get_unaligned_le16(&buf[1]);
+
+ dev_info(&client->dev,
+ "fw_id: 0x%x, plat_id: 0x%x, xml_id1: %04x, xml_id2: %04x\n",
+ param->fw_id, param->plat_id,
+ param->xmls_id1, param->xmls_id2);
+
+ return 0;
+}
+
+static int wdt87xx_validate_firmware(struct wdt87xx_data *wdt,
+ const struct firmware *fw)
+{
+ const void *fw_chunk;
+ u32 data1, data2;
+ u32 size;
+ u8 fw_chip_id;
+ u8 chip_id;
+
+ data1 = get_unaligned_le32(fw->data + FW_FOURCC1_OFFSET);
+ data2 = get_unaligned_le32(fw->data + FW_FOURCC2_OFFSET);
+ if (data1 != FOURCC_ID_RIFF || data2 != FOURCC_ID_WHIF) {
+ dev_err(&wdt->client->dev, "check fw tag failed\n");
+ return -EINVAL;
+ }
+
+ size = get_unaligned_le32(fw->data + FW_SIZE_OFFSET);
+ if (size != fw->size) {
+ dev_err(&wdt->client->dev,
+ "fw size mismatch: expected %d, actual %zu\n",
+ size, fw->size);
+ return -EINVAL;
+ }
+
+ /*
+ * Get the chip_id from the firmware. Make sure that it is the
+ * right controller to do the firmware and config update.
+ */
+ fw_chunk = wdt87xx_get_fw_chunk(fw, CHUNK_ID_FRWR);
+ if (!fw_chunk) {
+ dev_err(&wdt->client->dev,
+ "unable to locate firmware chunk\n");
+ return -EINVAL;
+ }
+
+ fw_chip_id = (get_unaligned_le32(fw_chunk +
+ FW_CHUNK_VERSION_OFFSET) >> 12) & 0xF;
+ chip_id = (wdt->param.fw_id >> 12) & 0xF;
+
+ if (fw_chip_id != chip_id) {
+ dev_err(&wdt->client->dev,
+ "fw version mismatch: fw %d vs. chip %d\n",
+ fw_chip_id, chip_id);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int wdt87xx_validate_fw_chunk(const void *data, int id)
+{
+ if (id == CHUNK_ID_FRWR) {
+ u32 fw_id;
+
+ fw_id = get_unaligned_le32(data + FW_CHUNK_PAYLOAD_OFFSET);
+ if (fw_id != WDT_FIRMWARE_ID)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wdt87xx_write_data(struct i2c_client *client, const char *data,
+ u32 address, int length)
+{
+ u16 packet_size;
+ int count = 0;
+ int error;
+ u8 pkt_buf[PKT_BUF_SIZE];
+
+ /* Address and length should be 4 bytes aligned */
+ if ((address & 0x3) != 0 || (length & 0x3) != 0) {
+ dev_err(&client->dev,
+ "addr & len must be 4 bytes aligned %x, %x\n",
+ address, length);
+ return -EINVAL;
+ }
+
+ while (length) {
+ packet_size = min(length, PACKET_SIZE);
+
+ pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE;
+ pkt_buf[CMD_TYPE_OFFSET] = VND_SET_DATA;
+ put_unaligned_le16(packet_size, &pkt_buf[CMD_INDEX_OFFSET]);
+ put_unaligned_le32(address, &pkt_buf[CMD_LENGTH_OFFSET]);
+ memcpy(&pkt_buf[CMD_DATA_OFFSET], data, packet_size);
+
+ error = wdt87xx_set_feature(client, pkt_buf, sizeof(pkt_buf));
+ if (error)
+ return error;
+
+ length -= packet_size;
+ data += packet_size;
+ address += packet_size;
+
+ /* Wait for the controller to finish the write */
+ mdelay(WDT_FLASH_WRITE_DELAY_MS);
+
+ if ((++count % 32) == 0) {
+ /* Delay for fw to clear watch dog */
+ msleep(20);
+ }
+ }
+
+ return 0;
+}
+
+static u16 misr(u16 cur_value, u8 new_value)
+{
+ u32 a, b;
+ u32 bit0;
+ u32 y;
+
+ a = cur_value;
+ b = new_value;
+ bit0 = a ^ (b & 1);
+ bit0 ^= a >> 1;
+ bit0 ^= a >> 2;
+ bit0 ^= a >> 4;
+ bit0 ^= a >> 5;
+ bit0 ^= a >> 7;
+ bit0 ^= a >> 11;
+ bit0 ^= a >> 15;
+ y = (a << 1) ^ b;
+ y = (y & ~1) | (bit0 & 1);
+
+ return (u16)y;
+}
+
+static u16 wdt87xx_calculate_checksum(const u8 *data, size_t length)
+{
+ u16 checksum = 0;
+ size_t i;
+
+ for (i = 0; i < length; i++)
+ checksum = misr(checksum, data[i]);
+
+ return checksum;
+}
+
+static int wdt87xx_get_checksum(struct i2c_client *client, u16 *checksum,
+ u32 address, int length)
+{
+ int error;
+ int time_delay;
+ u8 pkt_buf[PKT_BUF_SIZE];
+ u8 cmd_buf[CMD_BUF_SIZE];
+
+ error = wdt87xx_send_command(client, VND_SET_CHECKSUM_LENGTH, length);
+ if (error) {
+ dev_err(&client->dev, "failed to set checksum length\n");
+ return error;
+ }
+
+ error = wdt87xx_send_command(client, VND_SET_CHECKSUM_CALC, address);
+ if (error) {
+ dev_err(&client->dev, "failed to set checksum address\n");
+ return error;
+ }
+
+ /* Wait the operation to complete */
+ time_delay = DIV_ROUND_UP(length, 1024);
+ msleep(time_delay * 30);
+
+ memset(cmd_buf, 0, sizeof(cmd_buf));
+ cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_READ;
+ cmd_buf[CMD_TYPE_OFFSET] = VND_GET_CHECKSUM;
+ error = wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf));
+ if (error) {
+ dev_err(&client->dev, "failed to request checksum\n");
+ return error;
+ }
+
+ memse