Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
a5cba18c
Commit
a5cba18c
authored
Jul 03, 2015
by
Dmitry Torokhov
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'next' into for-linus
Prepare second round of input updates for 4.2 merge window.
parents
f7ebc4dc
d55d0b56
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1296 additions
and
76 deletions
+1296
-76
drivers/input/input.c
drivers/input/input.c
+9
-25
drivers/input/joystick/xpad.c
drivers/input/joystick/xpad.c
+65
-13
drivers/input/keyboard/imx_keypad.c
drivers/input/keyboard/imx_keypad.c
+3
-1
drivers/input/misc/axp20x-pek.c
drivers/input/misc/axp20x-pek.c
+6
-2
drivers/input/serio/Kconfig
drivers/input/serio/Kconfig
+1
-0
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Kconfig
+12
-0
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/Makefile
+1
-0
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/edt-ft5x06.c
+2
-7
drivers/input/touchscreen/of_touchscreen.c
drivers/input/touchscreen/of_touchscreen.c
+44
-25
drivers/input/touchscreen/tsc2005.c
drivers/input/touchscreen/tsc2005.c
+1
-1
drivers/input/touchscreen/wdt87xx_i2c.c
drivers/input/touchscreen/wdt87xx_i2c.c
+1149
-0
include/linux/input/touchscreen.h
include/linux/input/touchscreen.h
+3
-2
No files found.
drivers/input/input.c
View file @
a5cba18c
...
...
@@ -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
;
...
...
drivers/input/joystick/xpad.c
View file @
a5cba18c
...
...
@@ -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
...
...
drivers/input/keyboard/imx_keypad.c
View file @
a5cba18c
...
...
@@ -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
);
...
...
drivers/input/misc/axp20x-pek.c
View file @
a5cba18c
...
...
@@ -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_db
f
)
else
if
(
irq
==
axp20x_pek
->
irq_db
r
)
input_report_key
(
idev
,
KEY_POWER
,
false
);
input_sync
(
idev
);
...
...
drivers/input/serio/Kconfig
View file @
a5cba18c
...
...
@@ -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.
...
...
drivers/input/touchscreen/Kconfig
View file @
a5cba18c
...
...
@@ -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
...
...
drivers/input/touchscreen/Makefile
View file @
a5cba18c
...
...
@@ -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
...
...
drivers/input/touchscreen/edt-ft5x06.c
View file @
a5cba18c
...
...
@@ -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
;
...
...
drivers/input/touchscreen/of_touchscreen.c
View file @
a5cba18c
...
...
@@ -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
);
drivers/input/touchscreen/tsc2005.c
View file @
a5cba18c
...
...
@@ -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
;
...
...
drivers/input/touchscreen/wdt87xx_i2c.c
0 → 100644
View file @
a5cba18c
/*
* 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"
);
include/linux/input/touchscreen.h
View file @
a5cba18c
...
...
@@ -12,9 +12,10 @@
#include <linux/input.h>
#ifdef CONFIG_OF
void
touchscreen_parse_of_params
(
struct
input_dev
*
dev
);
void
touchscreen_parse_of_params
(
struct
input_dev
*
dev
,
bool
multitouch
);
#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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment