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
Kirill Smelkov
linux
Commits
cf45b5a2
Commit
cf45b5a2
authored
Jul 29, 2012
by
Dmitry Torokhov
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'next' into for-linus
Prepare second set of changes for 3.6 merge window.
parents
314820c9
c0394506
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1033 additions
and
3 deletions
+1033
-3
Documentation/input/edt-ft5x06.txt
Documentation/input/edt-ft5x06.txt
+54
-0
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.c
+22
-0
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.c
+19
-2
drivers/input/tablet/wacom_wac.h
drivers/input/tablet/wacom_wac.h
+2
-1
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Kconfig
+13
-0
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/Makefile
+1
-0
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/edt-ft5x06.c
+898
-0
include/linux/input/edt-ft5x06.h
include/linux/input/edt-ft5x06.h
+24
-0
No files found.
Documentation/input/edt-ft5x06.txt
0 → 100644
View file @
cf45b5a2
EDT ft5x06 based Polytouch devices
----------------------------------
The edt-ft5x06 driver is useful for the EDT "Polytouch" family of capacitive
touch screens. Note that it is *not* suitable for other devices based on the
focaltec ft5x06 devices, since they contain vendor-specific firmware. In
particular this driver is not suitable for the Nook tablet.
It has been tested with the following devices:
* EP0350M06
* EP0430M06
* EP0570M06
* EP0700M06
The driver allows configuration of the touch screen via a set of sysfs files:
/sys/class/input/eventX/device/device/threshold:
allows setting the "click"-threshold in the range from 20 to 80.
/sys/class/input/eventX/device/device/gain:
allows setting the sensitivity in the range from 0 to 31. Note that
lower values indicate higher sensitivity.
/sys/class/input/eventX/device/device/offset:
allows setting the edge compensation in the range from 0 to 31.
/sys/class/input/eventX/device/device/report_rate:
allows setting the report rate in the range from 3 to 14.
For debugging purposes the driver provides a few files in the debug
filesystem (if available in the kernel). In /sys/kernel/debug/edt_ft5x06
you'll find the following files:
num_x, num_y:
(readonly) contains the number of sensor fields in X- and
Y-direction.
mode:
allows switching the sensor between "factory mode" and "operation
mode" by writing "1" or "0" to it. In factory mode (1) it is
possible to get the raw data from the sensor. Note that in factory
mode regular events don't get delivered and the options described
above are unavailable.
raw_data:
contains num_x * num_y big endian 16 bit values describing the raw
values for each sensor field. Note that each read() call on this
files triggers a new readout. It is recommended to provide a buffer
big enough to contain num_x * num_y * 2 bytes.
Note that reading raw_data gives a I/O error when the device is not in factory
mode. The same happens when reading/writing to the parameter files when the
device is not in regular operation mode.
drivers/input/mouse/synaptics.c
View file @
cf45b5a2
...
...
@@ -40,11 +40,27 @@
* Note that newer firmware allows querying device for maximum useable
* coordinates.
*/
#define XMIN 0
#define XMAX 6143
#define YMIN 0
#define YMAX 6143
#define XMIN_NOMINAL 1472
#define XMAX_NOMINAL 5472
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
/* Size in bits of absolute position values reported by the hardware */
#define ABS_POS_BITS 13
/*
* Any position values from the hardware above the following limits are
* treated as "wrapped around negative" values that have been truncated to
* the 13-bit reporting range of the hardware. These are just reasonable
* guesses and can be adjusted if hardware is found that operates outside
* of these parameters.
*/
#define X_MAX_POSITIVE (((1 << ABS_POS_BITS) + XMAX) / 2)
#define Y_MAX_POSITIVE (((1 << ABS_POS_BITS) + YMAX) / 2)
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
...
...
@@ -588,6 +604,12 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw
->
right
=
(
buf
[
0
]
&
0x02
)
?
1
:
0
;
}
/* Convert wrap-around values to negative */
if
(
hw
->
x
>
X_MAX_POSITIVE
)
hw
->
x
-=
1
<<
ABS_POS_BITS
;
if
(
hw
->
y
>
Y_MAX_POSITIVE
)
hw
->
y
-=
1
<<
ABS_POS_BITS
;
return
0
;
}
...
...
drivers/input/tablet/wacom_wac.c
View file @
cf45b5a2
...
...
@@ -464,7 +464,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
t
=
(
data
[
6
]
<<
2
)
|
((
data
[
7
]
>>
6
)
&
3
);
if
((
features
->
type
>=
INTUOS4S
&&
features
->
type
<=
INTUOS4L
)
||
(
features
->
type
>=
INTUOS5S
&&
features
->
type
<=
INTUOS5L
)
||
features
->
type
==
WACOM_21UX2
||
features
->
type
==
WACOM_24HD
)
{
(
features
->
type
>=
WACOM_21UX2
&&
features
->
type
<=
WACOM_24HD
)
)
{
t
=
(
t
<<
1
)
|
(
data
[
1
]
&
1
);
}
input_report_abs
(
input
,
ABS_PRESSURE
,
t
);
...
...
@@ -614,7 +614,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
input_report_abs
(
input
,
ABS_MISC
,
0
);
}
}
else
{
if
(
features
->
type
==
WACOM_21UX2
)
{
if
(
features
->
type
==
WACOM_21UX2
||
features
->
type
==
WACOM_22HD
)
{
input_report_key
(
input
,
BTN_0
,
(
data
[
5
]
&
0x01
));
input_report_key
(
input
,
BTN_1
,
(
data
[
6
]
&
0x01
));
input_report_key
(
input
,
BTN_2
,
(
data
[
6
]
&
0x02
));
...
...
@@ -633,6 +633,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
input_report_key
(
input
,
BTN_Z
,
(
data
[
8
]
&
0x20
));
input_report_key
(
input
,
BTN_BASE
,
(
data
[
8
]
&
0x40
));
input_report_key
(
input
,
BTN_BASE2
,
(
data
[
8
]
&
0x80
));
if
(
features
->
type
==
WACOM_22HD
)
{
input_report_key
(
input
,
KEY_PROG1
,
data
[
9
]
&
0x01
);
input_report_key
(
input
,
KEY_PROG2
,
data
[
9
]
&
0x02
);
input_report_key
(
input
,
KEY_PROG3
,
data
[
9
]
&
0x04
);
}
}
else
{
input_report_key
(
input
,
BTN_0
,
(
data
[
5
]
&
0x01
));
input_report_key
(
input
,
BTN_1
,
(
data
[
5
]
&
0x02
));
...
...
@@ -1231,6 +1237,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
case
CINTIQ
:
case
WACOM_BEE
:
case
WACOM_21UX2
:
case
WACOM_22HD
:
case
WACOM_24HD
:
sync
=
wacom_intuos_irq
(
wacom_wac
);
break
;
...
...
@@ -1432,6 +1439,12 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
wacom_setup_cintiq
(
wacom_wac
);
break
;
case
WACOM_22HD
:
__set_bit
(
KEY_PROG1
,
input_dev
->
keybit
);
__set_bit
(
KEY_PROG2
,
input_dev
->
keybit
);
__set_bit
(
KEY_PROG3
,
input_dev
->
keybit
);
/* fall through */
case
WACOM_21UX2
:
__set_bit
(
BTN_A
,
input_dev
->
keybit
);
__set_bit
(
BTN_B
,
input_dev
->
keybit
);
...
...
@@ -1858,6 +1871,9 @@ static const struct wacom_features wacom_features_0xF0 =
static
const
struct
wacom_features
wacom_features_0xCC
=
{
"Wacom Cintiq 21UX2"
,
WACOM_PKGLEN_INTUOS
,
87200
,
65600
,
2047
,
63
,
WACOM_21UX2
,
WACOM_INTUOS3_RES
,
WACOM_INTUOS3_RES
};
static
const
struct
wacom_features
wacom_features_0xFA
=
{
"Wacom Cintiq 22HD"
,
WACOM_PKGLEN_INTUOS
,
95840
,
54260
,
2047
,
63
,
WACOM_22HD
,
WACOM_INTUOS3_RES
,
WACOM_INTUOS3_RES
};
static
const
struct
wacom_features
wacom_features_0x90
=
{
"Wacom ISDv4 90"
,
WACOM_PKGLEN_GRAPHIRE
,
26202
,
16325
,
255
,
0
,
TABLETPC
,
WACOM_INTUOS_RES
,
WACOM_INTUOS_RES
};
...
...
@@ -2075,6 +2091,7 @@ const struct usb_device_id wacom_ids[] = {
{
USB_DEVICE_WACOM
(
0xEF
)
},
{
USB_DEVICE_WACOM
(
0x47
)
},
{
USB_DEVICE_WACOM
(
0xF4
)
},
{
USB_DEVICE_WACOM
(
0xFA
)
},
{
USB_DEVICE_LENOVO
(
0x6004
)
},
{
}
};
...
...
drivers/input/tablet/wacom_wac.h
View file @
cf45b5a2
...
...
@@ -73,8 +73,9 @@ enum {
INTUOS5S
,
INTUOS5
,
INTUOS5L
,
WACOM_24HD
,
WACOM_21UX2
,
WACOM_22HD
,
WACOM_24HD
,
CINTIQ
,
WACOM_BEE
,
WACOM_MO
,
...
...
drivers/input/touchscreen/Kconfig
View file @
cf45b5a2
...
...
@@ -472,6 +472,19 @@ config TOUCHSCREEN_PENMOUNT
To compile this driver as a module, choose M here: the
module will be called penmount.
config TOUCHSCREEN_EDT_FT5X06
tristate "EDT FocalTech FT5x06 I2C Touchscreen support"
depends on I2C
help
Say Y here if you have an EDT "Polytouch" touchscreen based
on the FocalTech FT5x06 family of controllers connected to
your system.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called edt-ft5x06.
config TOUCHSCREEN_MIGOR
tristate "Renesas MIGO-R touchscreen"
depends on SH_MIGOR && I2C
...
...
drivers/input/touchscreen/Makefile
View file @
cf45b5a2
...
...
@@ -24,6 +24,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
obj-$(CONFIG_TOUCHSCREEN_DA9034)
+=
da9034-ts.o
obj-$(CONFIG_TOUCHSCREEN_DA9052)
+=
da9052_tsi.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)
+=
dynapro.o
obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06)
+=
edt-ft5x06.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)
+=
hampshire.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE)
+=
gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI)
+=
eeti_ts.o
...
...
drivers/input/touchscreen/edt-ft5x06.c
0 → 100644
View file @
cf45b5a2
/*
* Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This is a driver for the EDT "Polytouch" family of touch controllers
* based on the FocalTech FT5x06 line of chips.
*
* Development of this driver has been sponsored by Glyn:
* http://www.glyn.com/Products/Displays
*/
#include <linux/module.h>
#include <linux/ratelimit.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/input/mt.h>
#include <linux/input/edt-ft5x06.h>
#define MAX_SUPPORT_POINTS 5
#define WORK_REGISTER_THRESHOLD 0x00
#define WORK_REGISTER_REPORT_RATE 0x08
#define WORK_REGISTER_GAIN 0x30
#define WORK_REGISTER_OFFSET 0x31
#define WORK_REGISTER_NUM_X 0x33
#define WORK_REGISTER_NUM_Y 0x34
#define WORK_REGISTER_OPMODE 0x3c
#define FACTORY_REGISTER_OPMODE 0x01
#define TOUCH_EVENT_DOWN 0x00
#define TOUCH_EVENT_UP 0x01
#define TOUCH_EVENT_ON 0x02
#define TOUCH_EVENT_RESERVED 0x03
#define EDT_NAME_LEN 23
#define EDT_SWITCH_MODE_RETRIES 10
#define EDT_SWITCH_MODE_DELAY 5
/* msec */
#define EDT_RAW_DATA_RETRIES 100
#define EDT_RAW_DATA_DELAY 1
/* msec */
struct
edt_ft5x06_ts_data
{
struct
i2c_client
*
client
;
struct
input_dev
*
input
;
u16
num_x
;
u16
num_y
;
#if defined(CONFIG_DEBUG_FS)
struct
dentry
*
debug_dir
;
u8
*
raw_buffer
;
size_t
raw_bufsize
;
#endif
struct
mutex
mutex
;
bool
factory_mode
;
int
threshold
;
int
gain
;
int
offset
;
int
report_rate
;
char
name
[
EDT_NAME_LEN
];
};
static
int
edt_ft5x06_ts_readwrite
(
struct
i2c_client
*
client
,
u16
wr_len
,
u8
*
wr_buf
,
u16
rd_len
,
u8
*
rd_buf
)
{
struct
i2c_msg
wrmsg
[
2
];
int
i
=
0
;
int
ret
;
if
(
wr_len
)
{
wrmsg
[
i
].
addr
=
client
->
addr
;
wrmsg
[
i
].
flags
=
0
;
wrmsg
[
i
].
len
=
wr_len
;
wrmsg
[
i
].
buf
=
wr_buf
;
i
++
;
}
if
(
rd_len
)
{
wrmsg
[
i
].
addr
=
client
->
addr
;
wrmsg
[
i
].
flags
=
I2C_M_RD
;
wrmsg
[
i
].
len
=
rd_len
;
wrmsg
[
i
].
buf
=
rd_buf
;
i
++
;
}
ret
=
i2c_transfer
(
client
->
adapter
,
wrmsg
,
i
);
if
(
ret
<
0
)
return
ret
;
if
(
ret
!=
i
)
return
-
EIO
;
return
0
;
}
static
bool
edt_ft5x06_ts_check_crc
(
struct
edt_ft5x06_ts_data
*
tsdata
,
u8
*
buf
,
int
buflen
)
{
int
i
;
u8
crc
=
0
;
for
(
i
=
0
;
i
<
buflen
-
1
;
i
++
)
crc
^=
buf
[
i
];
if
(
crc
!=
buf
[
buflen
-
1
])
{
dev_err_ratelimited
(
&
tsdata
->
client
->
dev
,
"crc error: 0x%02x expected, got 0x%02x
\n
"
,
crc
,
buf
[
buflen
-
1
]);
return
false
;
}
return
true
;
}
static
irqreturn_t
edt_ft5x06_ts_isr
(
int
irq
,
void
*
dev_id
)
{
struct
edt_ft5x06_ts_data
*
tsdata
=
dev_id
;
struct
device
*
dev
=
&
tsdata
->
client
->
dev
;
u8
cmd
=
0xf9
;
u8
rdbuf
[
26
];
int
i
,
type
,
x
,
y
,
id
;
int
error
;
memset
(
rdbuf
,
0
,
sizeof
(
rdbuf
));
error
=
edt_ft5x06_ts_readwrite
(
tsdata
->
client
,
sizeof
(
cmd
),
&
cmd
,
sizeof
(
rdbuf
),
rdbuf
);
if
(
error
)
{
dev_err_ratelimited
(
dev
,
"Unable to fetch data, error: %d
\n
"
,
error
);
goto
out
;
}
if
(
rdbuf
[
0
]
!=
0xaa
||
rdbuf
[
1
]
!=
0xaa
||
rdbuf
[
2
]
!=
26
)
{
dev_err_ratelimited
(
dev
,
"Unexpected header: %02x%02x%02x!
\n
"
,
rdbuf
[
0
],
rdbuf
[
1
],
rdbuf
[
2
]);
goto
out
;
}
if
(
!
edt_ft5x06_ts_check_crc
(
tsdata
,
rdbuf
,
26
))
goto
out
;
for
(
i
=
0
;
i
<
MAX_SUPPORT_POINTS
;
i
++
)
{
u8
*
buf
=
&
rdbuf
[
i
*
4
+
5
];
bool
down
;
type
=
buf
[
0
]
>>
6
;
/* ignore Reserved events */
if
(
type
==
TOUCH_EVENT_RESERVED
)
continue
;
x
=
((
buf
[
0
]
<<
8
)
|
buf
[
1
])
&
0x0fff
;
y
=
((
buf
[
2
]
<<
8
)
|
buf
[
3
])
&
0x0fff
;
id
=
(
buf
[
2
]
>>
4
)
&
0x0f
;
down
=
(
type
!=
TOUCH_EVENT_UP
);
input_mt_slot
(
tsdata
->
input
,
id
);
input_mt_report_slot_state
(
tsdata
->
input
,
MT_TOOL_FINGER
,
down
);
if
(
!
down
)
continue
;
input_report_abs
(
tsdata
->
input
,
ABS_MT_POSITION_X
,
x
);
input_report_abs
(
tsdata
->
input
,
ABS_MT_POSITION_Y
,
y
);
}
input_mt_report_pointer_emulation
(
tsdata
->
input
,
true
);
input_sync
(
tsdata
->
input
);
out:
return
IRQ_HANDLED
;
}
static
int
edt_ft5x06_register_write
(
struct
edt_ft5x06_ts_data
*
tsdata
,
u8
addr
,
u8
value
)
{
u8
wrbuf
[
4
];
wrbuf
[
0
]
=
tsdata
->
factory_mode
?
0xf3
:
0xfc
;
wrbuf
[
1
]
=
tsdata
->
factory_mode
?
addr
&
0x7f
:
addr
&
0x3f
;
wrbuf
[
2
]
=
value
;
wrbuf
[
3
]
=
wrbuf
[
0
]
^
wrbuf
[
1
]
^
wrbuf
[
2
];
return
edt_ft5x06_ts_readwrite
(
tsdata
->
client
,
4
,
wrbuf
,
0
,
NULL
);
}
static
int
edt_ft5x06_register_read
(
struct
edt_ft5x06_ts_data
*
tsdata
,
u8
addr
)
{
u8
wrbuf
[
2
],
rdbuf
[
2
];
int
error
;
wrbuf
[
0
]
=
tsdata
->
factory_mode
?
0xf3
:
0xfc
;
wrbuf
[
1
]
=
tsdata
->
factory_mode
?
addr
&
0x7f
:
addr
&
0x3f
;
wrbuf
[
1
]
|=
tsdata
->
factory_mode
?
0x80
:
0x40
;
error
=
edt_ft5x06_ts_readwrite
(
tsdata
->
client
,
2
,
wrbuf
,
2
,
rdbuf
);
if
(
error
)
return
error
;
if
((
wrbuf
[
0
]
^
wrbuf
[
1
]
^
rdbuf
[
0
])
!=
rdbuf
[
1
])
{
dev_err
(
&
tsdata
->
client
->
dev
,
"crc error: 0x%02x expected, got 0x%02x
\n
"
,
wrbuf
[
0
]
^
wrbuf
[
1
]
^
rdbuf
[
0
],
rdbuf
[
1
]);
return
-
EIO
;
}
return
rdbuf
[
0
];
}
struct
edt_ft5x06_attribute
{
struct
device_attribute
dattr
;
size_t
field_offset
;
u8
limit_low
;
u8
limit_high
;
u8
addr
;
};
#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \
struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
.dattr = __ATTR(_field, _mode, \
edt_ft5x06_setting_show, \
edt_ft5x06_setting_store), \
.field_offset = \
offsetof(struct edt_ft5x06_ts_data, _field), \
.limit_low = _limit_low, \
.limit_high = _limit_high, \
.addr = _addr, \
}
static
ssize_t
edt_ft5x06_setting_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
edt_ft5x06_ts_data
*
tsdata
=
i2c_get_clientdata
(
client
);
struct
edt_ft5x06_attribute
*
attr
=
container_of
(
dattr
,
struct
edt_ft5x06_attribute
,
dattr
);
u8
*
field
=
(
u8
*
)((
char
*
)
tsdata
+
attr
->
field_offset
);
int
val
;
size_t
count
=
0
;
int
error
=
0
;
mutex_lock
(
&
tsdata
->
mutex
);
if
(
tsdata
->
factory_mode
)
{
error
=
-
EIO
;
goto
out
;
}
val
=
edt_ft5x06_register_read
(
tsdata
,
attr
->
addr
);
if
(
val
<
0
)
{
error
=
val
;
dev_err
(
&
tsdata
->
client
->
dev
,
"Failed to fetch attribute %s, error %d
\n
"
,
dattr
->
attr
.
name
,
error
);
goto
out
;
}
if
(
val
!=
*
field
)
{
dev_warn
(
&
tsdata
->
client
->
dev
,
"%s: read (%d) and stored value (%d) differ
\n
"
,
dattr
->
attr
.
name
,
val
,
*
field
);
*
field
=
val
;
}
count
=
scnprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
val
);
out:
mutex_unlock
(
&
tsdata
->
mutex
);
return
error
?:
count
;
}
static
ssize_t
edt_ft5x06_setting_store
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
const
char
*
buf
,
size_t
count
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
edt_ft5x06_ts_data
*
tsdata
=
i2c_get_clientdata
(
client
);
struct
edt_ft5x06_attribute
*
attr
=
container_of
(
dattr
,
struct
edt_ft5x06_attribute
,
dattr
);
u8
*
field
=
(
u8
*
)((
char
*
)
tsdata
+
attr
->
field_offset
);
unsigned
int
val
;
int
error
;
mutex_lock
(
&
tsdata
->
mutex
);
if
(
tsdata
->
factory_mode
)
{
error
=
-
EIO
;
goto
out
;
}
error
=
kstrtouint
(
buf
,
0
,
&
val
);
if
(
error
)
goto
out
;
if
(
val
<
attr
->
limit_low
||
val
>
attr
->
limit_high
)
{
error
=
-
ERANGE
;
goto
out
;
}
error
=
edt_ft5x06_register_write
(
tsdata
,
attr
->
addr
,
val
);
if
(
error
)
{
dev_err
(
&
tsdata
->
client
->
dev
,
"Failed to update attribute %s, error: %d
\n
"
,
dattr
->
attr
.
name
,
error
);
goto
out
;
}
*
field
=
val
;
out:
mutex_unlock
(
&
tsdata
->
mutex
);
return
error
?:
count
;
}
static
EDT_ATTR
(
gain
,
S_IWUSR
|
S_IRUGO
,
WORK_REGISTER_GAIN
,
0
,
31
);
static
EDT_ATTR
(
offset
,
S_IWUSR
|
S_IRUGO
,
WORK_REGISTER_OFFSET
,
0
,
31
);
static
EDT_ATTR
(
threshold
,
S_IWUSR
|
S_IRUGO
,
WORK_REGISTER_THRESHOLD
,
20
,
80
);
static
EDT_ATTR
(
report_rate
,
S_IWUSR
|
S_IRUGO
,
WORK_REGISTER_REPORT_RATE
,
3
,
14
);
static
struct
attribute
*
edt_ft5x06_attrs
[]
=
{
&
edt_ft5x06_attr_gain
.
dattr
.
attr
,
&
edt_ft5x06_attr_offset
.
dattr
.
attr
,
&
edt_ft5x06_attr_threshold
.
dattr
.
attr
,
&
edt_ft5x06_attr_report_rate
.
dattr
.
attr
,
NULL
};
static
const
struct
attribute_group
edt_ft5x06_attr_group
=
{
.
attrs
=
edt_ft5x06_attrs
,
};
#ifdef CONFIG_DEBUG_FS
static
int
edt_ft5x06_factory_mode
(
struct
edt_ft5x06_ts_data
*
tsdata
)
{
struct
i2c_client
*
client
=
tsdata
->
client
;
int
retries
=
EDT_SWITCH_MODE_RETRIES
;
int
ret
;
int
error
;
disable_irq
(
client
->
irq
);
if
(
!
tsdata
->
raw_buffer
)
{
tsdata
->
raw_bufsize
=
tsdata
->
num_x
*
tsdata
->
num_y
*
sizeof
(
u16
);
tsdata
->
raw_buffer
=
kzalloc
(
tsdata
->
raw_bufsize
,
GFP_KERNEL
);
if
(
!
tsdata
->
raw_buffer
)
{
error
=
-
ENOMEM
;
goto
err_out
;
}
}
/* mode register is 0x3c when in the work mode */
error
=
edt_ft5x06_register_write
(
tsdata
,
WORK_REGISTER_OPMODE
,
0x03
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"failed to switch to factory mode, error %d
\n
"
,
error
);
goto
err_out
;
}
tsdata
->
factory_mode
=
true
;
do
{
mdelay
(
EDT_SWITCH_MODE_DELAY
);
/* mode register is 0x01 when in factory mode */
ret
=
edt_ft5x06_register_read
(
tsdata
,
FACTORY_REGISTER_OPMODE
);
if
(
ret
==
0x03
)
break
;
}
while
(
--
retries
>
0
);
if
(
retries
==
0
)
{
dev_err
(
&
client
->
dev
,
"not in factory mode after %dms.
\n
"
,
EDT_SWITCH_MODE_RETRIES
*
EDT_SWITCH_MODE_DELAY
);
error
=
-
EIO
;
goto
err_out
;
}
return
0
;
err_out:
kfree
(
tsdata
->
raw_buffer
);
tsdata
->
raw_buffer
=
NULL
;
tsdata
->
factory_mode
=
false
;
enable_irq
(
client
->
irq
);
return
error
;
}
static
int
edt_ft5x06_work_mode
(
struct
edt_ft5x06_ts_data
*
tsdata
)
{
struct
i2c_client
*
client
=
tsdata
->
client
;
int
retries
=
EDT_SWITCH_MODE_RETRIES
;
int
ret
;
int
error
;
/* mode register is 0x01 when in the factory mode */
error
=
edt_ft5x06_register_write
(
tsdata
,
FACTORY_REGISTER_OPMODE
,
0x1
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"failed to switch to work mode, error: %d
\n
"
,
error
);
return
error
;
}
tsdata
->
factory_mode
=
false
;
do
{
mdelay
(
EDT_SWITCH_MODE_DELAY
);
/* mode register is 0x01 when in factory mode */
ret
=
edt_ft5x06_register_read
(
tsdata
,
WORK_REGISTER_OPMODE
);
if
(
ret
==
0x01
)
break
;
}
while
(
--
retries
>
0
);
if
(
retries
==
0
)
{
dev_err
(
&
client
->
dev
,
"not in work mode after %dms.
\n
"
,
EDT_SWITCH_MODE_RETRIES
*
EDT_SWITCH_MODE_DELAY
);
tsdata
->
factory_mode
=
true
;
return
-
EIO
;
}
if
(
tsdata
->
raw_buffer
)
kfree
(
tsdata
->
raw_buffer
);
tsdata
->
raw_buffer
=
NULL
;
/* restore parameters */
edt_ft5x06_register_write
(
tsdata
,
WORK_REGISTER_THRESHOLD
,
tsdata
->
threshold
);
edt_ft5x06_register_write
(
tsdata
,
WORK_REGISTER_GAIN
,
tsdata
->
gain
);
edt_ft5x06_register_write
(
tsdata
,
WORK_REGISTER_OFFSET
,
tsdata
->
offset
);
edt_ft5x06_register_write
(
tsdata
,
WORK_REGISTER_REPORT_RATE
,
tsdata
->
report_rate
);
enable_irq
(
client
->
irq
);
return
0
;
}
static
int
edt_ft5x06_debugfs_mode_get
(
void
*
data
,
u64
*
mode
)
{
struct
edt_ft5x06_ts_data
*
tsdata
=
data
;
*
mode
=
tsdata
->
factory_mode
;
return
0
;
};
static
int
edt_ft5x06_debugfs_mode_set
(
void
*
data
,
u64
mode
)
{
struct
edt_ft5x06_ts_data
*
tsdata
=
data
;
int
retval
=
0
;
if
(
mode
>
1
)
return
-
ERANGE
;
mutex_lock
(
&
tsdata
->
mutex
);
if
(
mode
!=
tsdata
->
factory_mode
)
{
retval
=
mode
?
edt_ft5x06_factory_mode
(
tsdata
)
:
edt_ft5x06_work_mode
(
tsdata
);
}
mutex_unlock
(
&
tsdata
->
mutex
);
return
retval
;
};
DEFINE_SIMPLE_ATTRIBUTE
(
debugfs_mode_fops
,
edt_ft5x06_debugfs_mode_get
,
edt_ft5x06_debugfs_mode_set
,
"%llu
\n
"
);
static
int
edt_ft5x06_debugfs_raw_data_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
file
->
private_data
=
inode
->
i_private
;
return
0
;
}
static
ssize_t
edt_ft5x06_debugfs_raw_data_read
(
struct
file
*
file
,
char
__user
*
buf
,
size_t
count
,
loff_t
*
off
)
{
struct
edt_ft5x06_ts_data
*
tsdata
=
file
->
private_data
;
struct
i2c_client
*
client
=
tsdata
->
client
;
int
retries
=
EDT_RAW_DATA_RETRIES
;
int
val
,
i
,
error
;
size_t
read
=
0
;
int
colbytes
;
char
wrbuf
[
3
];
u8
*
rdbuf
;
if
(
*
off
<
0
||
*
off
>=
tsdata
->
raw_bufsize
)
return
0
;
mutex_lock
(
&
tsdata
->
mutex
);
if
(
!
tsdata
->
factory_mode
||
!
tsdata
->
raw_buffer
)
{
error
=
-
EIO
;
goto
out
;
}
error
=
edt_ft5x06_register_write
(
tsdata
,
0x08
,
0x01
);
if
(
error
)
{
dev_dbg
(
&
client
->
dev
,
"failed to write 0x08 register, error %d
\n
"
,
error
);
goto
out
;
}
do
{
msleep
(
EDT_RAW_DATA_DELAY
);
val
=
edt_ft5x06_register_read
(
tsdata
,
0x08
);
if
(
val
<
1
)
break
;
}
while
(
--
retries
>
0
);
if
(
val
<
0
)
{
error
=
val
;
dev_dbg
(
&
client
->
dev
,
"failed to read 0x08 register, error %d
\n
"
,
error
);
goto
out
;
}
if
(
retries
==
0
)
{
dev_dbg
(
&
client
->
dev
,
"timed out waiting for register to settle
\n
"
);
error
=
-
ETIMEDOUT
;
goto
out
;
}
rdbuf
=
tsdata
->
raw_buffer
;
colbytes
=
tsdata
->
num_y
*
sizeof
(
u16
);
wrbuf
[
0
]
=
0xf5
;
wrbuf
[
1
]
=
0x0e
;
for
(
i
=
0
;
i
<
tsdata
->
num_x
;
i
++
)
{
wrbuf
[
2
]
=
i
;
/* column index */
error
=
edt_ft5x06_ts_readwrite
(
tsdata
->
client
,
sizeof
(
wrbuf
),
wrbuf
,
colbytes
,
rdbuf
);
if
(
error
)
goto
out
;
rdbuf
+=
colbytes
;
}
read
=
min_t
(
size_t
,
count
,
tsdata
->
raw_bufsize
-
*
off
);
error
=
copy_to_user
(
buf
,
tsdata
->
raw_buffer
+
*
off
,
read
);
if
(
!
error
)
*
off
+=
read
;
out:
mutex_unlock
(
&
tsdata
->
mutex
);
return
error
?:
read
;
};
static
const
struct
file_operations
debugfs_raw_data_fops
=
{
.
open
=
edt_ft5x06_debugfs_raw_data_open
,
.
read
=
edt_ft5x06_debugfs_raw_data_read
,
};
static
void
__devinit
edt_ft5x06_ts_prepare_debugfs
(
struct
edt_ft5x06_ts_data
*
tsdata
,
const
char
*
debugfs_name
)
{
tsdata
->
debug_dir
=
debugfs_create_dir
(
debugfs_name
,
NULL
);
if
(
!
tsdata
->
debug_dir
)
return
;
debugfs_create_u16
(
"num_x"
,
S_IRUSR
,
tsdata
->
debug_dir
,
&
tsdata
->
num_x
);
debugfs_create_u16
(
"num_y"
,
S_IRUSR
,
tsdata
->
debug_dir
,
&
tsdata
->
num_y
);
debugfs_create_file
(
"mode"
,
S_IRUSR
|
S_IWUSR
,
tsdata
->
debug_dir
,
tsdata
,
&
debugfs_mode_fops
);
debugfs_create_file
(
"raw_data"
,
S_IRUSR
,
tsdata
->
debug_dir
,
tsdata
,
&
debugfs_raw_data_fops
);
}
static
void
__devexit
edt_ft5x06_ts_teardown_debugfs
(
struct
edt_ft5x06_ts_data
*
tsdata
)
{
if
(
tsdata
->
debug_dir
)
debugfs_remove_recursive
(
tsdata
->
debug_dir
);
}
#else
static
inline
void
edt_ft5x06_ts_prepare_debugfs
(
struct
edt_ft5x06_ts_data
*
tsdata
,
const
char
*
debugfs_name
)
{
}
static
inline
void
edt_ft5x06_ts_teardown_debugfs
(
struct
edt_ft5x06_ts_data
*
tsdata
)
{
}
#endif
/* CONFIG_DEBUGFS */
static
int
__devinit
edt_ft5x06_ts_reset
(
struct
i2c_client
*
client
,
int
reset_pin
)
{
int
error
;
if
(
gpio_is_valid
(
reset_pin
))
{
/* this pulls reset down, enabling the low active reset */
error
=
gpio_request_one
(
reset_pin
,
GPIOF_OUT_INIT_LOW
,
"edt-ft5x06 reset"
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to request GPIO %d as reset pin, error %d
\n
"
,
reset_pin
,
error
);
return
error
;
}
mdelay
(
50
);
gpio_set_value
(
reset_pin
,
1
);
mdelay
(
100
);
}
return
0
;
}
static
int
__devinit
edt_ft5x06_ts_identify
(
struct
i2c_client
*
client
,
char
*
model_name
,
char
*
fw_version
)
{
u8
rdbuf
[
EDT_NAME_LEN
];
char
*
p
;
int
error
;
error
=
edt_ft5x06_ts_readwrite
(
client
,
1
,
"
\xbb
"
,
EDT_NAME_LEN
-
1
,
rdbuf
);
if
(
error
)
return
error
;
/* remove last '$' end marker */
rdbuf
[
EDT_NAME_LEN
-
1
]
=
'\0'
;
if
(
rdbuf
[
EDT_NAME_LEN
-
2
]
==
'$'
)
rdbuf
[
EDT_NAME_LEN
-
2
]
=
'\0'
;
/* look for Model/Version separator */
p
=
strchr
(
rdbuf
,
'*'
);
if
(
p
)
*
p
++
=
'\0'
;
strlcpy
(
model_name
,
rdbuf
+
1
,
EDT_NAME_LEN
);
strlcpy
(
fw_version
,
p
?
p
:
""
,
EDT_NAME_LEN
);
return
0
;
}
#define EDT_ATTR_CHECKSET(name, reg) \
if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \
pdata->name <= edt_ft5x06_attr_##name.limit_high) \
edt_ft5x06_register_write(tsdata, reg, pdata->name)
static
void
__devinit
edt_ft5x06_ts_get_defaults
(
struct
edt_ft5x06_ts_data
*
tsdata
,
const
struct
edt_ft5x06_platform_data
*
pdata
)
{
if
(
!
pdata
->
use_parameters
)
return
;
/* pick up defaults from the platform data */
EDT_ATTR_CHECKSET
(
threshold
,
WORK_REGISTER_THRESHOLD
);
EDT_ATTR_CHECKSET
(
gain
,
WORK_REGISTER_GAIN
);
EDT_ATTR_CHECKSET
(
offset
,
WORK_REGISTER_OFFSET
);
EDT_ATTR_CHECKSET
(
report_rate
,
WORK_REGISTER_REPORT_RATE
);
}
static
void
__devinit
edt_ft5x06_ts_get_parameters
(
struct
edt_ft5x06_ts_data
*
tsdata
)
{
tsdata
->
threshold
=
edt_ft5x06_register_read
(
tsdata
,
WORK_REGISTER_THRESHOLD
);
tsdata
->
gain
=
edt_ft5x06_register_read
(
tsdata
,
WORK_REGISTER_GAIN
);
tsdata
->
offset
=
edt_ft5x06_register_read
(
tsdata
,
WORK_REGISTER_OFFSET
);
tsdata
->
report_rate
=
edt_ft5x06_register_read
(
tsdata
,
WORK_REGISTER_REPORT_RATE
);
tsdata
->
num_x
=
edt_ft5x06_register_read
(
tsdata
,
WORK_REGISTER_NUM_X
);
tsdata
->
num_y
=
edt_ft5x06_register_read
(
tsdata
,
WORK_REGISTER_NUM_Y
);
}
static
int
__devinit
edt_ft5x06_ts_probe
(
struct
i2c_client
*
client
,
const
struct
i2c_device_id
*
id
)
{
const
struct
edt_ft5x06_platform_data
*
pdata
=
client
->
dev
.
platform_data
;
struct
edt_ft5x06_ts_data
*
tsdata
;
struct
input_dev
*
input
;
int
error
;
char
fw_version
[
EDT_NAME_LEN
];
dev_dbg
(
&
client
->
dev
,
"probing for EDT FT5x06 I2C
\n
"
);
if
(
!
pdata
)
{
dev_err
(
&
client
->
dev
,
"no platform data?
\n
"
);
return
-
EINVAL
;
}
error
=
edt_ft5x06_ts_reset
(
client
,
pdata
->
reset_pin
);
if
(
error
)
return
error
;
if
(
gpio_is_valid
(
pdata
->
irq_pin
))
{
error
=
gpio_request_one
(
pdata
->
irq_pin
,
GPIOF_IN
,
"edt-ft5x06 irq"
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to request GPIO %d, error %d
\n
"
,
pdata
->
irq_pin
,
error
);
return
error
;
}
}
tsdata
=
kzalloc
(
sizeof
(
*
tsdata
),
GFP_KERNEL
);
input
=
input_allocate_device
();
if
(
!
tsdata
||
!
input
)
{
dev_err
(
&
client
->
dev
,
"failed to allocate driver data.
\n
"
);
error
=
-
ENOMEM
;
goto
err_free_mem
;
}
mutex_init
(
&
tsdata
->
mutex
);
tsdata
->
client
=
client
;
tsdata
->
input
=
input
;
tsdata
->
factory_mode
=
false
;
error
=
edt_ft5x06_ts_identify
(
client
,
tsdata
->
name
,
fw_version
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"touchscreen probe failed
\n
"
);
goto
err_free_mem
;
}
edt_ft5x06_ts_get_defaults
(
tsdata
,
pdata
);
edt_ft5x06_ts_get_parameters
(
tsdata
);
dev_dbg
(
&
client
->
dev
,
"Model
\"
%s
\"
, Rev.
\"
%s
\"
, %dx%d sensors
\n
"
,
tsdata
->
name
,
fw_version
,
tsdata
->
num_x
,
tsdata
->
num_y
);
input
->
name
=
tsdata
->
name
;
input
->
id
.
bustype
=
BUS_I2C
;
input
->
dev
.
parent
=
&
client
->
dev
;
__set_bit
(
EV_SYN
,
input
->
evbit
);
__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
);
error
=
input_mt_init_slots
(
input
,
MAX_SUPPORT_POINTS
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Unable to init MT slots.
\n
"
);
goto
err_free_mem
;
}
input_set_drvdata
(
input
,
tsdata
);
i2c_set_clientdata
(
client
,
tsdata
);
error
=
request_threaded_irq
(
client
->
irq
,
NULL
,
edt_ft5x06_ts_isr
,
IRQF_TRIGGER_FALLING
|
IRQF_ONESHOT
,
client
->
name
,
tsdata
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Unable to request touchscreen IRQ.
\n
"
);
goto
err_free_mem
;
}
error
=
sysfs_create_group
(
&
client
->
dev
.
kobj
,
&
edt_ft5x06_attr_group
);
if
(
error
)
goto
err_free_irq
;
error
=
input_register_device
(
input
);
if
(
error
)
goto
err_remove_attrs
;
edt_ft5x06_ts_prepare_debugfs
(
tsdata
,
dev_driver_string
(
&
client
->
dev
));
device_init_wakeup
(
&
client
->
dev
,
1
);
dev_dbg
(
&
client
->
dev
,
"EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.
\n
"
,
pdata
->
irq_pin
,
pdata
->
reset_pin
);
return
0
;
err_remove_attrs:
sysfs_remove_group
(
&
client
->
dev
.
kobj
,
&
edt_ft5x06_attr_group
);
err_free_irq:
free_irq
(
client
->
irq
,
tsdata
);
err_free_mem:
input_free_device
(
input
);
kfree
(
tsdata
);
if
(
gpio_is_valid
(
pdata
->
irq_pin
))
gpio_free
(
pdata
->
irq_pin
);
return
error
;
}
static
int
__devexit
edt_ft5x06_ts_remove
(
struct
i2c_client
*
client
)
{
const
struct
edt_ft5x06_platform_data
*
pdata
=
dev_get_platdata
(
&
client
->
dev
);
struct
edt_ft5x06_ts_data
*
tsdata
=
i2c_get_clientdata
(
client
);
edt_ft5x06_ts_teardown_debugfs
(
tsdata
);
sysfs_remove_group
(
&
client
->
dev
.
kobj
,
&
edt_ft5x06_attr_group
);
free_irq
(
client
->
irq
,
tsdata
);
input_unregister_device
(
tsdata
->
input
);
if
(
gpio_is_valid
(
pdata
->
irq_pin
))
gpio_free
(
pdata
->
irq_pin
);
if
(
gpio_is_valid
(
pdata
->
reset_pin
))
gpio_free
(
pdata
->
reset_pin
);
kfree
(
tsdata
->
raw_buffer
);
kfree
(
tsdata
);
return
0
;
}
#ifdef CONFIG_PM_SLEEP
static
int
edt_ft5x06_ts_suspend
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
if
(
device_may_wakeup
(
dev
))
enable_irq_wake
(
client
->
irq
);
return
0
;
}
static
int
edt_ft5x06_ts_resume
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
if
(
device_may_wakeup
(
dev
))
disable_irq_wake
(
client
->
irq
);
return
0
;
}
#endif
static
SIMPLE_DEV_PM_OPS
(
edt_ft5x06_ts_pm_ops
,
edt_ft5x06_ts_suspend
,
edt_ft5x06_ts_resume
);
static
const
struct
i2c_device_id
edt_ft5x06_ts_id
[]
=
{
{
"edt-ft5x06"
,
0
},
{
}
};
MODULE_DEVICE_TABLE
(
i2c
,
edt_ft5x06_ts_id
);
static
struct
i2c_driver
edt_ft5x06_ts_driver
=
{
.
driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"edt_ft5x06"
,
.
pm
=
&
edt_ft5x06_ts_pm_ops
,
},
.
id_table
=
edt_ft5x06_ts_id
,
.
probe
=
edt_ft5x06_ts_probe
,
.
remove
=
__devexit_p
(
edt_ft5x06_ts_remove
),
};
module_i2c_driver
(
edt_ft5x06_ts_driver
);
MODULE_AUTHOR
(
"Simon Budig <simon.budig@kernelconcepts.de>"
);
MODULE_DESCRIPTION
(
"EDT FT5x06 I2C Touchscreen Driver"
);
MODULE_LICENSE
(
"GPL"
);
include/linux/input/edt-ft5x06.h
0 → 100644
View file @
cf45b5a2
#ifndef _EDT_FT5X06_H
#define _EDT_FT5X06_H
/*
* Copyright (c) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
struct
edt_ft5x06_platform_data
{
int
irq_pin
;
int
reset_pin
;
/* startup defaults for operational parameters */
bool
use_parameters
;
u8
gain
;
u8
threshold
;
u8
offset
;
u8
report_rate
;
};
#endif
/* _EDT_FT5X06_H */
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