Commit ec0337e0 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull second round of input updates from Dmitry Torokhov:
 "A new driver for Weida wdt87xx touch controllers, and a bunch of
  fixups for other drivers"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: wdt87xx_i2c - add a scaling factor for TOUCH_MAJOR event
  Input: wdt87xx_i2c - remove stray newline in diagnostic message
  Input: arc_ps2 - add HAS_IOMEM dependency
  Input: wdt87xx_i2c - fix format warning
  Input: improve parsing OF parameters for touchscreens
  Input: edt-ft5x06 - mark as direct input device
  Input: use for_each_set_bit() where appropriate
  Input: add a driver for wdt87xx touchscreen controller
  Input: axp20x-pek - fix reporting button state as inverted
  Input: xpad - re-send LED command on present event
  Input: xpad - set the LEDs properly on XBox Wireless controllers
  Input: imx_keypad - check for clk_prepare_enable() error
parents 5c65e7be a5cba18c
...@@ -677,12 +677,9 @@ static void input_dev_release_keys(struct input_dev *dev) ...@@ -677,12 +677,9 @@ static void input_dev_release_keys(struct input_dev *dev)
int code; int code;
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
for (code = 0; code <= KEY_MAX; code++) { for_each_set_bit(code, dev->key, KEY_CNT)
if (is_event_supported(code, dev->keybit, KEY_MAX) && input_pass_event(dev, EV_KEY, code, 0);
__test_and_clear_bit(code, dev->key)) { memset(dev->key, 0, sizeof(dev->key));
input_pass_event(dev, EV_KEY, code, 0);
}
}
input_pass_event(dev, EV_SYN, SYN_REPORT, 1); 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) ...@@ -1626,10 +1623,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
if (!test_bit(EV_##type, dev->evbit)) \ if (!test_bit(EV_##type, dev->evbit)) \
break; \ break; \
\ \
for (i = 0; i < type##_MAX; i++) { \ for_each_set_bit(i, dev->bits##bit, type##_CNT) { \
if (!test_bit(i, dev->bits##bit)) \
continue; \
\
active = test_bit(i, dev->bits); \ active = test_bit(i, dev->bits); \
if (!active && !on) \ if (!active && !on) \
continue; \ continue; \
...@@ -1980,22 +1974,12 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev) ...@@ -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 */ events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */
if (test_bit(EV_ABS, dev->evbit)) { if (test_bit(EV_ABS, dev->evbit))
for (i = 0; i < ABS_CNT; i++) { for_each_set_bit(i, dev->absbit, ABS_CNT)
if (test_bit(i, dev->absbit)) { events += input_is_mt_axis(i) ? mt_slots : 1;
if (input_is_mt_axis(i))
events += mt_slots;
else
events++;
}
}
}
if (test_bit(EV_REL, dev->evbit)) { if (test_bit(EV_REL, dev->evbit))
for (i = 0; i < REL_CNT; i++) events += bitmap_weight(dev->relbit, REL_CNT);
if (test_bit(i, dev->relbit))
events++;
}
/* Make room for KEY and MSC events */ /* Make room for KEY and MSC events */
events += 7; events += 7;
......
...@@ -344,6 +344,7 @@ struct usb_xpad { ...@@ -344,6 +344,7 @@ struct usb_xpad {
int mapping; /* map d-pad to buttons or to axes */ int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */ 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, ...@@ -488,6 +489,8 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
input_sync(dev); input_sync(dev);
} }
static void xpad_identify_controller(struct usb_xpad *xpad);
/* /*
* xpad360w_process_packet * xpad360w_process_packet
* *
...@@ -510,6 +513,11 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha ...@@ -510,6 +513,11 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
if (data[1] & 0x80) { if (data[1] & 0x80) {
xpad->pad_present = 1; xpad->pad_present = 1;
usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
/*
* Light up the segment corresponding to
* controller number.
*/
xpad_identify_controller(xpad);
} else } else
xpad->pad_present = 0; xpad->pad_present = 0;
} }
...@@ -881,17 +889,63 @@ struct xpad_led { ...@@ -881,17 +889,63 @@ struct xpad_led {
struct usb_xpad *xpad; 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) static void xpad_send_led_command(struct usb_xpad *xpad, int command)
{ {
if (command >= 0 && command < 14) { command %= 16;
mutex_lock(&xpad->odata_mutex);
mutex_lock(&xpad->odata_mutex);
switch (xpad->xtype) {
case XTYPE_XBOX360:
xpad->odata[0] = 0x01; xpad->odata[0] = 0x01;
xpad->odata[1] = 0x03; xpad->odata[1] = 0x03;
xpad->odata[2] = command; xpad->odata[2] = command;
xpad->irq_out->transfer_buffer_length = 3; xpad->irq_out->transfer_buffer_length = 3;
usb_submit_urb(xpad->irq_out, GFP_KERNEL); break;
mutex_unlock(&xpad->odata_mutex); 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, static void xpad_led_set(struct led_classdev *led_cdev,
...@@ -905,22 +959,21 @@ 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 int xpad_led_probe(struct usb_xpad *xpad)
{ {
static atomic_t led_seq = ATOMIC_INIT(-1); static atomic_t led_seq = ATOMIC_INIT(-1);
unsigned long led_no;
struct xpad_led *led; struct xpad_led *led;
struct led_classdev *led_cdev; struct led_classdev *led_cdev;
int error; int error;
if (xpad->xtype != XTYPE_XBOX360) if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W)
return 0; return 0;
xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL);
if (!led) if (!led)
return -ENOMEM; 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->xpad = xpad;
led_cdev = &led->led_cdev; led_cdev = &led->led_cdev;
...@@ -934,10 +987,8 @@ static int xpad_led_probe(struct usb_xpad *xpad) ...@@ -934,10 +987,8 @@ static int xpad_led_probe(struct usb_xpad *xpad)
return error; return error;
} }
/* /* Light up the segment corresponding to controller number */
* Light up the segment corresponding to controller number xpad_identify_controller(xpad);
*/
xpad_send_led_command(xpad, (led_no % 4) + 2);
return 0; return 0;
} }
...@@ -954,6 +1005,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) ...@@ -954,6 +1005,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad)
#else #else
static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } static int xpad_led_probe(struct usb_xpad *xpad) { return 0; }
static void xpad_led_disconnect(struct usb_xpad *xpad) { } static void xpad_led_disconnect(struct usb_xpad *xpad) { }
static void xpad_identify_controller(struct usb_xpad *xpad) { }
#endif #endif
......
...@@ -506,7 +506,9 @@ static int imx_keypad_probe(struct platform_device *pdev) ...@@ -506,7 +506,9 @@ static int imx_keypad_probe(struct platform_device *pdev)
input_set_drvdata(input_dev, keypad); input_set_drvdata(input_dev, keypad);
/* Ensure that the keypad will stay dormant until opened */ /* 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); imx_keypad_inhibit(keypad);
clk_disable_unprepare(keypad->clk); clk_disable_unprepare(keypad->clk);
......
...@@ -167,9 +167,13 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr) ...@@ -167,9 +167,13 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
struct input_dev *idev = pwr; struct input_dev *idev = pwr;
struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); 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); 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_report_key(idev, KEY_POWER, false);
input_sync(idev); input_sync(idev);
......
...@@ -244,6 +244,7 @@ config SERIO_PS2MULT ...@@ -244,6 +244,7 @@ config SERIO_PS2MULT
config SERIO_ARC_PS2 config SERIO_ARC_PS2
tristate "ARC PS/2 support" tristate "ARC PS/2 support"
depends on HAS_IOMEM
help help
Say Y here if you have an ARC FPGA platform with a PS/2 Say Y here if you have an ARC FPGA platform with a PS/2
controller in it. controller in it.
......
...@@ -658,6 +658,18 @@ config TOUCHSCREEN_PIXCIR ...@@ -658,6 +658,18 @@ config TOUCHSCREEN_PIXCIR
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called pixcir_i2c_ts. 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 config TOUCHSCREEN_WM831X
tristate "Support for WM831x touchscreen controllers" tristate "Support for WM831x touchscreen controllers"
depends on MFD_WM831X depends on MFD_WM831X
......
...@@ -72,6 +72,7 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o ...@@ -72,6 +72,7 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.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_WM831X) += wm831x-ts.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
......
...@@ -1035,20 +1035,15 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, ...@@ -1035,20 +1035,15 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input->id.bustype = BUS_I2C; input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev; 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, input_set_abs_params(input, ABS_MT_POSITION_X,
0, tsdata->num_x * 64 - 1, 0, 0); 0, tsdata->num_x * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0); 0, tsdata->num_y * 64 - 1, 0, 0);
if (!pdata) 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) { if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n"); dev_err(&client->dev, "Unable to init MT slots.\n");
return error; return error;
......
...@@ -14,14 +14,22 @@ ...@@ -14,14 +14,22 @@
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include <linux/input/touchscreen.h> #include <linux/input/touchscreen.h>
static u32 of_get_optional_u32(struct device_node *np, static bool touchscreen_get_prop_u32(struct device_node *np,
const char *property) 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, static void touchscreen_set_params(struct input_dev *dev,
...@@ -54,34 +62,45 @@ 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 * input device accordingly. The function keeps previously setuped default
* values if no value is specified via DT. * 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; 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); input_alloc_absinfo(dev);
if (!dev->absinfo) if (!dev->absinfo)
return; return;
maximum = of_get_optional_u32(np, "touchscreen-size-x"); axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x"); data_present = touchscreen_get_prop_u32(np, "touchscreen-size-x",
if (maximum || fuzz) { input_abs_get_max(dev, axis),
touchscreen_set_params(dev, ABS_X, maximum, fuzz); &maximum) |
touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz); 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"); axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y"); data_present = touchscreen_get_prop_u32(np, "touchscreen-size-y",
if (maximum || fuzz) { input_abs_get_max(dev, axis),
touchscreen_set_params(dev, ABS_Y, maximum, fuzz); &maximum) |
touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz); 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"); axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure"); data_present = touchscreen_get_prop_u32(np, "touchscreen-max-pressure",
if (maximum || fuzz) { input_abs_get_max(dev, axis),
touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz); &maximum) |
touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz); 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); EXPORT_SYMBOL(touchscreen_parse_of_params);
...@@ -709,7 +709,7 @@ static int tsc2005_probe(struct spi_device *spi) ...@@ -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); input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
if (np) if (np)
touchscreen_parse_of_params(input_dev); touchscreen_parse_of_params(input_dev, false);
input_dev->open = tsc2005_open; input_dev->open = tsc2005_open;
input_dev->close = tsc2005_close; input_dev->close = tsc2005_close;
......
/*
* 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;
}
memset(pkt_buf, 0, sizeof(pkt_buf));
pkt_buf[CMD_REPORT_ID_OFFSET] = VND_READ_DATA;
error = wdt87xx_get_feature(client, pkt_buf, sizeof(pkt_buf));
if (error) {
dev_err(&client->dev, "failed to read checksum\n");
return error;
}
*checksum = get_unaligned_le16(&pkt_buf[CMD_DATA_OFFSET]);
return 0;
}
static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk)
{
u32 start_addr = get_unaligned_le32(chunk + FW_CHUNK_TGT_START_OFFSET);
u32 size = get_unaligned_le32(chunk + FW_CHUNK_PAYLOAD_LEN_OFFSET);
const void *data = chunk + FW_CHUNK_PAYLOAD_OFFSET;
int error;
int err1;
int page_size;
int retry = 0;
u16 device_checksum, firmware_checksum;
dev_dbg(&client->dev, "start 4k page program\n");
error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_STOP);
if (error) {
dev_err(&client->dev, "stop report mode failed\n");
return error;
}
error = wdt87xx_send_command(client, VND_CMD_SFUNL, 0);
if (error) {
dev_err(&client->dev, "unlock failed\n");
goto out_enable_reporting;
}
mdelay(10);
while (size) {
dev_dbg(&client->dev, "%s: %x, %x\n", __func__,
start_addr, size);
page_size = min_t(u32, size, PG_SIZE);
size -= page_size;
for (retry = 0; retry < MAX_RETRIES; retry++) {
error = wdt87xx_send_command(client, VND_CMD_ERASE,
start_addr);
if (error) {
dev_err(&client->dev,
"erase failed at %#08x\n", start_addr);
break;
}
msleep(50);
error = wdt87xx_write_data(client, data, start_addr,
page_size);
if (error) {
dev_err(&client->dev,
"write failed at %#08x (%d bytes)\n",
start_addr, page_size);
break;
}
error = wdt87xx_get_checksum(client, &device_checksum,
start_addr, page_size);
if (error) {
dev_err(&client->dev,
"failed to retrieve checksum for %#08x (len: %d)\n",
start_addr, page_size);
break;
}
firmware_checksum =
wdt87xx_calculate_checksum(data, page_size);
if (device_checksum == firmware_checksum)
break;
dev_err(&client->dev,
"checksum fail: %d vs %d, retry %d\n",
device_checksum, firmware_checksum, retry);
}
if (retry == MAX_RETRIES) {
dev_err(&client->dev, "page write failed\n");
error = -EIO;
goto out_lock_device;
}
start_addr = start_addr + page_size;
data = data + page_size;
}
out_lock_device:
err1 = wdt87xx_send_command(client, VND_CMD_SFLCK, 0);
if (err1)
dev_err(&client->dev, "lock failed\n");
mdelay(10);
out_enable_reporting:
err1 = wdt87xx_send_command(client, VND_CMD_START, 0);
if (err1)
dev_err(&client->dev, "start to report failed\n");
return error ? error : err1;
}
static int wdt87xx_load_chunk(struct i2c_client *client,
const struct firmware *fw, u32 ck_id)
{
const void *chunk;
int error;
chunk = wdt87xx_get_fw_chunk(fw, ck_id);
if (!chunk) {
dev_err(&client->dev, "unable to locate chunk (type %d)\n",
ck_id);
return -EINVAL;
}
error = wdt87xx_validate_fw_chunk(chunk, ck_id);
if (error) {
dev_err(&client->dev, "invalid chunk (type %d): %d\n",
ck_id, error);
return error;
}
error = wdt87xx_write_firmware(client, chunk);
if (error) {
dev_err(&client->dev,
"failed to write fw chunk (type %d): %d\n",
ck_id, error);
return error;
}
return 0;
}
static int wdt87xx_do_update_firmware(struct i2c_client *client,
const struct firmware *fw,
unsigned int chunk_id)
{
struct wdt87xx_data *wdt = i2c_get_clientdata(client);
int error;
error = wdt87xx_validate_firmware(wdt, fw);
if (error)
return error;
error = mutex_lock_interruptible(&wdt->fw_mutex);
if (error)
return error;
disable_irq(client->irq);
error = wdt87xx_load_chunk(client, fw, chunk_id);
if (error) {
dev_err(&client->dev,
"firmware load failed (type: %d): %d\n",
chunk_id, error);
goto out;
}
error = wdt87xx_sw_reset(client);
if (error) {
dev_err(&client->dev, "soft reset failed: %d\n", error);
goto out;
}
/* Refresh the parameters */
error = wdt87xx_get_sysparam(client, &wdt->param);
if (error)
dev_err(&client->dev,
"failed to refresh system paramaters: %d\n", error);
out:
enable_irq(client->irq);
mutex_unlock(&wdt->fw_mutex);
return error ? error : 0;
}
static int wdt87xx_update_firmware(struct device *dev,
const char *fw_name, unsigned int chunk_id)
{
struct i2c_client *client = to_i2c_client(dev);
const struct firmware *fw;
int error;
error = request_firmware(&fw, fw_name, dev);
if (error) {
dev_err(&client->dev, "unable to retrieve firmware %s: %d\n",
fw_name, error);
return error;
}
error = wdt87xx_do_update_firmware(client, fw, chunk_id);
release_firmware(fw);
return error ? error : 0;
}
static ssize_t config_csum_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct wdt87xx_data *wdt = i2c_get_clientdata(client);
u32 cfg_csum;
cfg_csum = wdt->param.xmls_id1;
cfg_csum = (cfg_csum << 16) | wdt->param.xmls_id2;
return scnprintf(buf, PAGE_SIZE, "%x\n", cfg_csum);
}
static ssize_t fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct wdt87xx_data *wdt = i2c_get_clientdata(client);
return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.fw_id);
}
static ssize_t plat_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct wdt87xx_data *wdt = i2c_get_clientdata(client);
return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.plat_id);
}
static ssize_t update_config_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int error;
error = wdt87xx_update_firmware(dev, WDT87XX_CFG_NAME, CHUNK_ID_CNFG);
return error ? error : count;
}
static ssize_t update_fw_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int error;
error = wdt87xx_update_firmware(dev, WDT87XX_FW_NAME, CHUNK_ID_FRWR);
return error ? error : count;
}
static DEVICE_ATTR_RO(config_csum);
static DEVICE_ATTR_RO(fw_version);
static DEVICE_ATTR_RO(plat_id);
static DEVICE_ATTR_WO(update_config);
static DEVICE_ATTR_WO(update_fw);
static struct attribute *wdt87xx_attrs[] = {
&dev_attr_config_csum.attr,
&dev_attr_fw_version.attr,
&dev_attr_plat_id.attr,
&dev_attr_update_config.attr,
&dev_attr_update_fw.attr,
NULL
};
static const struct attribute_group wdt87xx_attr_group = {
.attrs = wdt87xx_attrs,
};
static void wdt87xx_report_contact(struct input_dev *input,
struct wdt87xx_sys_param *param,
u8 *buf)
{
int finger_id;
u32 x, y, w;
u8 p;
finger_id = (buf[FINGER_EV_V1_OFFSET_ID] >> 3) - 1;
if (finger_id < 0)
return;
/* Check if this is an active contact */
if (!(buf[FINGER_EV_V1_OFFSET_ID] & 0x1))
return;
w = buf[FINGER_EV_V1_OFFSET_W];
w *= param->scaling_factor;
p = buf[FINGER_EV_V1_OFFSET_P];
x = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_X);
y = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_Y);
y = DIV_ROUND_CLOSEST(y * param->phy_h, param->phy_w);
/* Refuse incorrect coordinates */
if (x > param->max_x || y > param->max_y)
return;
dev_dbg(input->dev.parent, "tip on (%d), x(%d), y(%d)\n",
finger_id, x, y);
input_mt_slot(input, finger_id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, w);
input_report_abs(input, ABS_MT_PRESSURE, p);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
}
static irqreturn_t wdt87xx_ts_interrupt(int irq, void *dev_id)
{
struct wdt87xx_data *wdt = dev_id;
struct i2c_client *client = wdt->client;
int i, fingers;
int error;
u8 raw_buf[WDT_V1_RAW_BUF_COUNT] = {0};
error = i2c_master_recv(client, raw_buf, WDT_V1_RAW_BUF_COUNT);
if (error < 0) {
dev_err(&client->dev, "read v1 raw data failed: %d\n", error);
goto irq_exit;
}
fingers = raw_buf[TOUCH_PK_V1_OFFSET_FNGR_NUM];
if (!fingers)
goto irq_exit;
for (i = 0; i < WDT_MAX_FINGER; i++)
wdt87xx_report_contact(wdt->input,
&wdt->param,
&raw_buf[TOUCH_PK_V1_OFFSET_EVENT +
i * FINGER_EV_V1_SIZE]);
input_mt_sync_frame(wdt->input);
input_sync(wdt->input);
irq_exit:
return IRQ_HANDLED;
}
static int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt)
{
struct device *dev = &wdt->client->dev;
struct input_dev *input;
unsigned int res = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS, wdt->param.phy_w);
int error;
input = devm_input_allocate_device(dev);
if (!input) {
dev_err(dev, "failed to allocate input device\n");
return -ENOMEM;
}
wdt->input = input;
input->name = "WDT87xx Touchscreen";
input->id.bustype = BUS_I2C;
input->phys = wdt->phys;
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
wdt->param.max_x, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
wdt->param.max_y, 0, 0);
input_abs_set_res(input, ABS_MT_POSITION_X, res);
input_abs_set_res(input, ABS_MT_POSITION_Y, res);
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
0, wdt->param.max_x, 0, 0);
input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
input_mt_init_slots(input, WDT_MAX_FINGER,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
error = input_register_device(input);
if (error) {
dev_err(dev, "failed to register input device: %d\n", error);
return error;
}
return 0;
}
static int wdt87xx_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wdt87xx_data *wdt;
int error;
dev_dbg(&client->dev, "adapter=%d, client irq: %d\n",
client->adapter->nr, client->irq);
/* Check if the I2C function is ok in this adaptor */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENXIO;
wdt = devm_kzalloc(&client->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
wdt->client = client;
mutex_init(&wdt->fw_mutex);
i2c_set_clientdata(client, wdt);
snprintf(wdt->phys, sizeof(wdt->phys), "i2c-%u-%04x/input0",
client->adapter->nr, client->addr);
error = wdt87xx_get_sysparam(client, &wdt->param);
if (error)
return error;
error = wdt87xx_ts_create_input_device(wdt);
if (error)
return error;
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, wdt87xx_ts_interrupt,
IRQF_ONESHOT,
client->name, wdt);
if (error) {
dev_err(&client->dev, "request irq failed: %d\n", error);
return error;
}
error = sysfs_create_group(&client->dev.kobj, &wdt87xx_attr_group);
if (error) {
dev_err(&client->dev, "create sysfs failed: %d\n", error);
return error;
}
return 0;
}
static int wdt87xx_ts_remove(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &wdt87xx_attr_group);
return 0;
}
static int __maybe_unused wdt87xx_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
int error;
disable_irq(client->irq);
error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_IDLE);
if (error) {
enable_irq(client->irq);
dev_err(&client->dev,
"failed to stop device when suspending: %d\n",
error);
return error;
}
return 0;
}
static int __maybe_unused wdt87xx_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
int error;
/*
* The chip may have been reset while system is resuming,
* give it some time to settle.
*/
mdelay(100);
error = wdt87xx_send_command(client, VND_CMD_START, 0);
if (error)
dev_err(&client->dev,
"failed to start device when resuming: %d\n",
error);
enable_irq(client->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(wdt87xx_pm_ops, wdt87xx_suspend, wdt87xx_resume);
static const struct i2c_device_id wdt87xx_dev_id[] = {
{ WDT87XX_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wdt87xx_dev_id);
static const struct acpi_device_id wdt87xx_acpi_id[] = {
{ "WDHT0001", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, wdt87xx_acpi_id);
static struct i2c_driver wdt87xx_driver = {
.probe = wdt87xx_ts_probe,
.remove = wdt87xx_ts_remove,
.id_table = wdt87xx_dev_id,
.driver = {
.name = WDT87XX_NAME,
.pm = &wdt87xx_pm_ops,
.acpi_match_table = ACPI_PTR(wdt87xx_acpi_id),
},
};
module_i2c_driver(wdt87xx_driver);
MODULE_AUTHOR("HN Chen <hn.chen@weidahitech.com>");
MODULE_DESCRIPTION("WeidaHiTech WDT87XX Touchscreen driver");
MODULE_VERSION(WDT87XX_DRV_VER);
MODULE_LICENSE("GPL");
...@@ -12,9 +12,10 @@ ...@@ -12,9 +12,10 @@
#include <linux/input.h> #include <linux/input.h>
#ifdef CONFIG_OF #ifdef CONFIG_OF
void touchscreen_parse_of_params(struct input_dev *dev); void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch);
#else #else
static inline void touchscreen_parse_of_params(struct input_dev *dev) static inline void touchscreen_parse_of_params(struct input_dev *dev,
bool multitouch)
{ {
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment