diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-07-03 23:56:31 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-07-03 23:56:31 -0700 |
commit | a5cba18c4de288335a975b5d57b68f6787330985 (patch) | |
tree | 9d1e46eb1e53807f9d76a1ee25a5cd7a15ae00f5 | |
parent | f7ebc4dcdeb1be7209d94a8c0dc602ab24e36ae1 (diff) | |
parent | d55d0b56b9c3a0d94ca6cc1643063610c138dde9 (diff) |
Merge branch 'next' into for-linus
Prepare second round of input updates for 4.2 merge window.
-rw-r--r-- | drivers/input/input.c | 34 | ||||
-rw-r--r-- | drivers/input/joystick/xpad.c | 78 | ||||
-rw-r--r-- | drivers/input/keyboard/imx_keypad.c | 4 | ||||
-rw-r--r-- | drivers/input/misc/axp20x-pek.c | 8 | ||||
-rw-r--r-- | drivers/input/serio/Kconfig | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/edt-ft5x06.c | 9 | ||||
-rw-r--r-- | drivers/input/touchscreen/of_touchscreen.c | 69 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2005.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/wdt87xx_i2c.c | 1149 | ||||
-rw-r--r-- | include/linux/input/touchscreen.h | 5 |
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 a398d636ca39..aa2b5f21b89b 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; |