Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
def179c2
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 3 年多
通知
13
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
def179c2
编写于
3月 16, 2011
作者:
D
Dmitry Torokhov
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'tsc2005' into next
上级
49851ca0
5cb81d19
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
809 addition
and
0 deletion
+809
-0
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Kconfig
+11
-0
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/Makefile
+1
-0
drivers/input/touchscreen/tsc2005.c
drivers/input/touchscreen/tsc2005.c
+756
-0
include/linux/spi/tsc2005.h
include/linux/spi/tsc2005.h
+41
-0
未找到文件。
drivers/input/touchscreen/Kconfig
浏览文件 @
def179c2
...
...
@@ -639,6 +639,17 @@ config TOUCHSCREEN_TOUCHIT213
To compile this driver as a module, choose M here: the
module will be called touchit213.
config TOUCHSCREEN_TSC2005
tristate "TSC2005 based touchscreens"
depends on SPI_MASTER
help
Say Y here if you have a TSC2005 based touchscreen.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called tsc2005.
config TOUCHSCREEN_TSC2007
tristate "TSC2007 based touchscreens"
depends on I2C
...
...
drivers/input/touchscreen/Makefile
浏览文件 @
def179c2
...
...
@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)
+=
touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)
+=
touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)
+=
touchwin.o
obj-$(CONFIG_TOUCHSCREEN_TSC2005)
+=
tsc2005.o
obj-$(CONFIG_TOUCHSCREEN_TSC2007)
+=
tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400)
+=
ucb1400_ts.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)
+=
wacom_w8001.o
...
...
drivers/input/touchscreen/tsc2005.c
0 → 100644
浏览文件 @
def179c2
/*
* TSC2005 touchscreen driver
*
* Copyright (C) 2006-2010 Nokia Corporation
*
* Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
* based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/spi/spi.h>
#include <linux/spi/tsc2005.h>
/*
* The touchscreen interface operates as follows:
*
* 1) Pen is pressed against the touchscreen.
* 2) TSC2005 performs AD conversion.
* 3) After the conversion is done TSC2005 drives DAV line down.
* 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled.
* 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2
* values.
* 6) tsc2005_irq_thread() reports coordinates to input layer and sets up
* tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms).
* 7) When the penup timer expires, there have not been touch or DAV interrupts
* during the last 40ms which means the pen has been lifted.
*
* ESD recovery via a hardware reset is done if the TSC2005 doesn't respond
* after a configurable period (in ms) of activity. If esd_timeout is 0, the
* watchdog is disabled.
*/
/* control byte 1 */
#define TSC2005_CMD 0x80
#define TSC2005_CMD_NORMAL 0x00
#define TSC2005_CMD_STOP 0x01
#define TSC2005_CMD_12BIT 0x04
/* control byte 0 */
#define TSC2005_REG_READ 0x0001
#define TSC2005_REG_PND0 0x0002
#define TSC2005_REG_X 0x0000
#define TSC2005_REG_Y 0x0008
#define TSC2005_REG_Z1 0x0010
#define TSC2005_REG_Z2 0x0018
#define TSC2005_REG_TEMP_HIGH 0x0050
#define TSC2005_REG_CFR0 0x0060
#define TSC2005_REG_CFR1 0x0068
#define TSC2005_REG_CFR2 0x0070
/* configuration register 0 */
#define TSC2005_CFR0_PRECHARGE_276US 0x0040
#define TSC2005_CFR0_STABTIME_1MS 0x0300
#define TSC2005_CFR0_CLOCK_1MHZ 0x1000
#define TSC2005_CFR0_RESOLUTION12 0x2000
#define TSC2005_CFR0_PENMODE 0x8000
#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \
TSC2005_CFR0_CLOCK_1MHZ | \
TSC2005_CFR0_RESOLUTION12 | \
TSC2005_CFR0_PRECHARGE_276US | \
TSC2005_CFR0_PENMODE)
/* bits common to both read and write of configuration register 0 */
#define TSC2005_CFR0_RW_MASK 0x3fff
/* configuration register 1 */
#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003
#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS
/* configuration register 2 */
#define TSC2005_CFR2_MAVE_Z 0x0004
#define TSC2005_CFR2_MAVE_Y 0x0008
#define TSC2005_CFR2_MAVE_X 0x0010
#define TSC2005_CFR2_AVG_7 0x0800
#define TSC2005_CFR2_MEDIUM_15 0x3000
#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \
TSC2005_CFR2_MAVE_Y | \
TSC2005_CFR2_MAVE_Z | \
TSC2005_CFR2_MEDIUM_15 | \
TSC2005_CFR2_AVG_7)
#define MAX_12BIT 0xfff
#define TSC2005_SPI_MAX_SPEED_HZ 10000000
#define TSC2005_PENUP_TIME_MS 40
struct
tsc2005_spi_rd
{
struct
spi_transfer
spi_xfer
;
u32
spi_tx
;
u32
spi_rx
;
};
struct
tsc2005
{
struct
spi_device
*
spi
;
struct
spi_message
spi_read_msg
;
struct
tsc2005_spi_rd
spi_x
;
struct
tsc2005_spi_rd
spi_y
;
struct
tsc2005_spi_rd
spi_z1
;
struct
tsc2005_spi_rd
spi_z2
;
struct
input_dev
*
idev
;
char
phys
[
32
];
struct
mutex
mutex
;
/* raw copy of previous x,y,z */
int
in_x
;
int
in_y
;
int
in_z1
;
int
in_z2
;
spinlock_t
lock
;
struct
timer_list
penup_timer
;
unsigned
int
esd_timeout
;
struct
delayed_work
esd_work
;
unsigned
long
last_valid_interrupt
;
unsigned
int
x_plate_ohm
;
bool
opened
;
bool
suspended
;
bool
pen_down
;
void
(
*
set_reset
)(
bool
enable
);
};
static
int
tsc2005_cmd
(
struct
tsc2005
*
ts
,
u8
cmd
)
{
u8
tx
=
TSC2005_CMD
|
TSC2005_CMD_12BIT
|
cmd
;
struct
spi_transfer
xfer
=
{
.
tx_buf
=
&
tx
,
.
len
=
1
,
.
bits_per_word
=
8
,
};
struct
spi_message
msg
;
int
error
;
spi_message_init
(
&
msg
);
spi_message_add_tail
(
&
xfer
,
&
msg
);
error
=
spi_sync
(
ts
->
spi
,
&
msg
);
if
(
error
)
{
dev_err
(
&
ts
->
spi
->
dev
,
"%s: failed, command: %x, error: %d
\n
"
,
__func__
,
cmd
,
error
);
return
error
;
}
return
0
;
}
static
int
tsc2005_write
(
struct
tsc2005
*
ts
,
u8
reg
,
u16
value
)
{
u32
tx
=
((
reg
|
TSC2005_REG_PND0
)
<<
16
)
|
value
;
struct
spi_transfer
xfer
=
{
.
tx_buf
=
&
tx
,
.
len
=
4
,
.
bits_per_word
=
24
,
};
struct
spi_message
msg
;
int
error
;
spi_message_init
(
&
msg
);
spi_message_add_tail
(
&
xfer
,
&
msg
);
error
=
spi_sync
(
ts
->
spi
,
&
msg
);
if
(
error
)
{
dev_err
(
&
ts
->
spi
->
dev
,
"%s: failed, register: %x, value: %x, error: %d
\n
"
,
__func__
,
reg
,
value
,
error
);
return
error
;
}
return
0
;
}
static
void
tsc2005_setup_read
(
struct
tsc2005_spi_rd
*
rd
,
u8
reg
,
bool
last
)
{
memset
(
rd
,
0
,
sizeof
(
*
rd
));
rd
->
spi_tx
=
(
reg
|
TSC2005_REG_READ
)
<<
16
;
rd
->
spi_xfer
.
tx_buf
=
&
rd
->
spi_tx
;
rd
->
spi_xfer
.
rx_buf
=
&
rd
->
spi_rx
;
rd
->
spi_xfer
.
len
=
4
;
rd
->
spi_xfer
.
bits_per_word
=
24
;
rd
->
spi_xfer
.
cs_change
=
!
last
;
}
static
int
tsc2005_read
(
struct
tsc2005
*
ts
,
u8
reg
,
u16
*
value
)
{
struct
tsc2005_spi_rd
spi_rd
;
struct
spi_message
msg
;
int
error
;
tsc2005_setup_read
(
&
spi_rd
,
reg
,
true
);
spi_message_init
(
&
msg
);
spi_message_add_tail
(
&
spi_rd
.
spi_xfer
,
&
msg
);
error
=
spi_sync
(
ts
->
spi
,
&
msg
);
if
(
error
)
return
error
;
*
value
=
spi_rd
.
spi_rx
;
return
0
;
}
static
void
tsc2005_update_pen_state
(
struct
tsc2005
*
ts
,
int
x
,
int
y
,
int
pressure
)
{
if
(
pressure
)
{
input_report_abs
(
ts
->
idev
,
ABS_X
,
x
);
input_report_abs
(
ts
->
idev
,
ABS_Y
,
y
);
input_report_abs
(
ts
->
idev
,
ABS_PRESSURE
,
pressure
);
if
(
!
ts
->
pen_down
)
{
input_report_key
(
ts
->
idev
,
BTN_TOUCH
,
!!
pressure
);
ts
->
pen_down
=
true
;
}
}
else
{
input_report_abs
(
ts
->
idev
,
ABS_PRESSURE
,
0
);
if
(
ts
->
pen_down
)
{
input_report_key
(
ts
->
idev
,
BTN_TOUCH
,
0
);
ts
->
pen_down
=
false
;
}
}
input_sync
(
ts
->
idev
);
dev_dbg
(
&
ts
->
spi
->
dev
,
"point(%4d,%4d), pressure (%4d)
\n
"
,
x
,
y
,
pressure
);
}
static
irqreturn_t
tsc2005_irq_thread
(
int
irq
,
void
*
_ts
)
{
struct
tsc2005
*
ts
=
_ts
;
unsigned
long
flags
;
unsigned
int
pressure
;
u32
x
,
y
;
u32
z1
,
z2
;
int
error
;
/* read the coordinates */
error
=
spi_sync
(
ts
->
spi
,
&
ts
->
spi_read_msg
);
if
(
unlikely
(
error
))
goto
out
;
x
=
ts
->
spi_x
.
spi_rx
;
y
=
ts
->
spi_y
.
spi_rx
;
z1
=
ts
->
spi_z1
.
spi_rx
;
z2
=
ts
->
spi_z2
.
spi_rx
;
/* validate position */
if
(
unlikely
(
x
>
MAX_12BIT
||
y
>
MAX_12BIT
))
goto
out
;
/* Skip reading if the pressure components are out of range */
if
(
unlikely
(
z1
==
0
||
z2
>
MAX_12BIT
||
z1
>=
z2
))
goto
out
;
/*
* Skip point if this is a pen down with the exact same values as
* the value before pen-up - that implies SPI fed us stale data
*/
if
(
!
ts
->
pen_down
&&
ts
->
in_x
==
x
&&
ts
->
in_y
==
y
&&
ts
->
in_z1
==
z1
&&
ts
->
in_z2
==
z2
)
{
goto
out
;
}
/*
* At this point we are happy we have a valid and useful reading.
* Remember it for later comparisons. We may now begin downsampling.
*/
ts
->
in_x
=
x
;
ts
->
in_y
=
y
;
ts
->
in_z1
=
z1
;
ts
->
in_z2
=
z2
;
/* Compute touch pressure resistance using equation #1 */
pressure
=
x
*
(
z2
-
z1
)
/
z1
;
pressure
=
pressure
*
ts
->
x_plate_ohm
/
4096
;
if
(
unlikely
(
pressure
>
MAX_12BIT
))
goto
out
;
spin_lock_irqsave
(
&
ts
->
lock
,
flags
);
tsc2005_update_pen_state
(
ts
,
x
,
y
,
pressure
);
mod_timer
(
&
ts
->
penup_timer
,
jiffies
+
msecs_to_jiffies
(
TSC2005_PENUP_TIME_MS
));
spin_unlock_irqrestore
(
&
ts
->
lock
,
flags
);
ts
->
last_valid_interrupt
=
jiffies
;
out:
return
IRQ_HANDLED
;
}
static
void
tsc2005_penup_timer
(
unsigned
long
data
)
{
struct
tsc2005
*
ts
=
(
struct
tsc2005
*
)
data
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
ts
->
lock
,
flags
);
tsc2005_update_pen_state
(
ts
,
0
,
0
,
0
);
spin_unlock_irqrestore
(
&
ts
->
lock
,
flags
);
}
static
void
tsc2005_start_scan
(
struct
tsc2005
*
ts
)
{
tsc2005_write
(
ts
,
TSC2005_REG_CFR0
,
TSC2005_CFR0_INITVALUE
);
tsc2005_write
(
ts
,
TSC2005_REG_CFR1
,
TSC2005_CFR1_INITVALUE
);
tsc2005_write
(
ts
,
TSC2005_REG_CFR2
,
TSC2005_CFR2_INITVALUE
);
tsc2005_cmd
(
ts
,
TSC2005_CMD_NORMAL
);
}
static
void
tsc2005_stop_scan
(
struct
tsc2005
*
ts
)
{
tsc2005_cmd
(
ts
,
TSC2005_CMD_STOP
);
}
/* must be called with ts->mutex held */
static
void
__tsc2005_disable
(
struct
tsc2005
*
ts
)
{
tsc2005_stop_scan
(
ts
);
disable_irq
(
ts
->
spi
->
irq
);
del_timer_sync
(
&
ts
->
penup_timer
);
cancel_delayed_work_sync
(
&
ts
->
esd_work
);
enable_irq
(
ts
->
spi
->
irq
);
}
/* must be called with ts->mutex held */
static
void
__tsc2005_enable
(
struct
tsc2005
*
ts
)
{
tsc2005_start_scan
(
ts
);
if
(
ts
->
esd_timeout
&&
ts
->
set_reset
)
{
ts
->
last_valid_interrupt
=
jiffies
;
schedule_delayed_work
(
&
ts
->
esd_work
,
round_jiffies
(
jiffies
+
msecs_to_jiffies
(
ts
->
esd_timeout
)));
}
}
static
ssize_t
tsc2005_selftest_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
spi_device
*
spi
=
to_spi_device
(
dev
);
struct
tsc2005
*
ts
=
spi_get_drvdata
(
spi
);
u16
temp_high
;
u16
temp_high_orig
;
u16
temp_high_test
;
bool
success
=
true
;
int
error
;
mutex_lock
(
&
ts
->
mutex
);
/*
* Test TSC2005 communications via temp high register.
*/
__tsc2005_disable
(
ts
);
error
=
tsc2005_read
(
ts
,
TSC2005_REG_TEMP_HIGH
,
&
temp_high_orig
);
if
(
error
)
{
dev_warn
(
dev
,
"selftest failed: read error %d
\n
"
,
error
);
success
=
false
;
goto
out
;
}
temp_high_test
=
(
temp_high_orig
-
1
)
&
MAX_12BIT
;
error
=
tsc2005_write
(
ts
,
TSC2005_REG_TEMP_HIGH
,
temp_high_test
);
if
(
error
)
{
dev_warn
(
dev
,
"selftest failed: write error %d
\n
"
,
error
);
success
=
false
;
goto
out
;
}
error
=
tsc2005_read
(
ts
,
TSC2005_REG_TEMP_HIGH
,
&
temp_high
);
if
(
error
)
{
dev_warn
(
dev
,
"selftest failed: read error %d after write
\n
"
,
error
);
success
=
false
;
goto
out
;
}
if
(
temp_high
!=
temp_high_test
)
{
dev_warn
(
dev
,
"selftest failed: %d != %d
\n
"
,
temp_high
,
temp_high_test
);
success
=
false
;
}
/* hardware reset */
ts
->
set_reset
(
false
);
usleep_range
(
100
,
500
);
/* only 10us required */
ts
->
set_reset
(
true
);
if
(
!
success
)
goto
out
;
/* test that the reset really happened */
error
=
tsc2005_read
(
ts
,
TSC2005_REG_TEMP_HIGH
,
&
temp_high
);
if
(
error
)
{
dev_warn
(
dev
,
"selftest failed: read error %d after reset
\n
"
,
error
);
success
=
false
;
goto
out
;
}
if
(
temp_high
!=
temp_high_orig
)
{
dev_warn
(
dev
,
"selftest failed after reset: %d != %d
\n
"
,
temp_high
,
temp_high_orig
);
success
=
false
;
}
out:
__tsc2005_enable
(
ts
);
mutex_unlock
(
&
ts
->
mutex
);
return
sprintf
(
buf
,
"%d
\n
"
,
success
);
}
static
DEVICE_ATTR
(
selftest
,
S_IRUGO
,
tsc2005_selftest_show
,
NULL
);
static
struct
attribute
*
tsc2005_attrs
[]
=
{
&
dev_attr_selftest
.
attr
,
NULL
};
static
mode_t
tsc2005_attr_is_visible
(
struct
kobject
*
kobj
,
struct
attribute
*
attr
,
int
n
)
{
struct
device
*
dev
=
container_of
(
kobj
,
struct
device
,
kobj
);
struct
spi_device
*
spi
=
to_spi_device
(
dev
);
struct
tsc2005
*
ts
=
spi_get_drvdata
(
spi
);
mode_t
mode
=
attr
->
mode
;
if
(
attr
==
&
dev_attr_selftest
.
attr
)
{
if
(
!
ts
->
set_reset
)
mode
=
0
;
}
return
mode
;
}
static
const
struct
attribute_group
tsc2005_attr_group
=
{
.
is_visible
=
tsc2005_attr_is_visible
,
.
attrs
=
tsc2005_attrs
,
};
static
void
tsc2005_esd_work
(
struct
work_struct
*
work
)
{
struct
tsc2005
*
ts
=
container_of
(
work
,
struct
tsc2005
,
esd_work
.
work
);
int
error
;
u16
r
;
mutex_lock
(
&
ts
->
mutex
);
if
(
time_is_after_jiffies
(
ts
->
last_valid_interrupt
+
msecs_to_jiffies
(
ts
->
esd_timeout
)))
goto
out
;
/* We should be able to read register without disabling interrupts. */
error
=
tsc2005_read
(
ts
,
TSC2005_REG_CFR0
,
&
r
);
if
(
!
error
&&
!
((
r
^
TSC2005_CFR0_INITVALUE
)
&
TSC2005_CFR0_RW_MASK
))
{
goto
out
;
}
/*
* If we could not read our known value from configuration register 0
* then we should reset the controller as if from power-up and start
* scanning again.
*/
dev_info
(
&
ts
->
spi
->
dev
,
"TSC2005 not responding - resetting
\n
"
);
disable_irq
(
ts
->
spi
->
irq
);
del_timer_sync
(
&
ts
->
penup_timer
);
tsc2005_update_pen_state
(
ts
,
0
,
0
,
0
);
ts
->
set_reset
(
false
);
usleep_range
(
100
,
500
);
/* only 10us required */
ts
->
set_reset
(
true
);
enable_irq
(
ts
->
spi
->
irq
);
tsc2005_start_scan
(
ts
);
out:
/* re-arm the watchdog */
schedule_delayed_work
(
&
ts
->
esd_work
,
round_jiffies
(
jiffies
+
msecs_to_jiffies
(
ts
->
esd_timeout
)));
mutex_unlock
(
&
ts
->
mutex
);
}
static
int
tsc2005_open
(
struct
input_dev
*
input
)
{
struct
tsc2005
*
ts
=
input_get_drvdata
(
input
);
mutex_lock
(
&
ts
->
mutex
);
if
(
!
ts
->
suspended
)
__tsc2005_enable
(
ts
);
ts
->
opened
=
true
;
mutex_unlock
(
&
ts
->
mutex
);
return
0
;
}
static
void
tsc2005_close
(
struct
input_dev
*
input
)
{
struct
tsc2005
*
ts
=
input_get_drvdata
(
input
);
mutex_lock
(
&
ts
->
mutex
);
if
(
!
ts
->
suspended
)
__tsc2005_disable
(
ts
);
ts
->
opened
=
false
;
mutex_unlock
(
&
ts
->
mutex
);
}
static
void
__devinit
tsc2005_setup_spi_xfer
(
struct
tsc2005
*
ts
)
{
tsc2005_setup_read
(
&
ts
->
spi_x
,
TSC2005_REG_X
,
false
);
tsc2005_setup_read
(
&
ts
->
spi_y
,
TSC2005_REG_Y
,
false
);
tsc2005_setup_read
(
&
ts
->
spi_z1
,
TSC2005_REG_Z1
,
false
);
tsc2005_setup_read
(
&
ts
->
spi_z2
,
TSC2005_REG_Z2
,
true
);
spi_message_init
(
&
ts
->
spi_read_msg
);
spi_message_add_tail
(
&
ts
->
spi_x
.
spi_xfer
,
&
ts
->
spi_read_msg
);
spi_message_add_tail
(
&
ts
->
spi_y
.
spi_xfer
,
&
ts
->
spi_read_msg
);
spi_message_add_tail
(
&
ts
->
spi_z1
.
spi_xfer
,
&
ts
->
spi_read_msg
);
spi_message_add_tail
(
&
ts
->
spi_z2
.
spi_xfer
,
&
ts
->
spi_read_msg
);
}
static
int
__devinit
tsc2005_probe
(
struct
spi_device
*
spi
)
{
const
struct
tsc2005_platform_data
*
pdata
=
spi
->
dev
.
platform_data
;
struct
tsc2005
*
ts
;
struct
input_dev
*
input_dev
;
unsigned
int
max_x
,
max_y
,
max_p
;
unsigned
int
fudge_x
,
fudge_y
,
fudge_p
;
int
error
;
if
(
!
pdata
)
{
dev_dbg
(
&
spi
->
dev
,
"no platform data
\n
"
);
return
-
ENODEV
;
}
fudge_x
=
pdata
->
ts_x_fudge
?
:
4
;
fudge_y
=
pdata
->
ts_y_fudge
?
:
8
;
fudge_p
=
pdata
->
ts_pressure_fudge
?
:
2
;
max_x
=
pdata
->
ts_x_max
?
:
MAX_12BIT
;
max_y
=
pdata
->
ts_y_max
?
:
MAX_12BIT
;
max_p
=
pdata
->
ts_pressure_max
?
:
MAX_12BIT
;
if
(
spi
->
irq
<=
0
)
{
dev_dbg
(
&
spi
->
dev
,
"no irq
\n
"
);
return
-
ENODEV
;
}
spi
->
mode
=
SPI_MODE_0
;
spi
->
bits_per_word
=
8
;
if
(
!
spi
->
max_speed_hz
)
spi
->
max_speed_hz
=
TSC2005_SPI_MAX_SPEED_HZ
;
error
=
spi_setup
(
spi
);
if
(
error
)
return
error
;
ts
=
kzalloc
(
sizeof
(
*
ts
),
GFP_KERNEL
);
input_dev
=
input_allocate_device
();
if
(
!
ts
||
!
input_dev
)
{
error
=
-
ENOMEM
;
goto
err_free_mem
;
}
ts
->
spi
=
spi
;
ts
->
idev
=
input_dev
;
ts
->
x_plate_ohm
=
pdata
->
ts_x_plate_ohm
?
:
280
;
ts
->
esd_timeout
=
pdata
->
esd_timeout_ms
;
ts
->
set_reset
=
pdata
->
set_reset
;
mutex_init
(
&
ts
->
mutex
);
spin_lock_init
(
&
ts
->
lock
);
setup_timer
(
&
ts
->
penup_timer
,
tsc2005_penup_timer
,
(
unsigned
long
)
ts
);
INIT_DELAYED_WORK
(
&
ts
->
esd_work
,
tsc2005_esd_work
);
tsc2005_setup_spi_xfer
(
ts
);
snprintf
(
ts
->
phys
,
sizeof
(
ts
->
phys
),
"%s/input-ts"
,
dev_name
(
&
spi
->
dev
));
input_dev
->
name
=
"TSC2005 touchscreen"
;
input_dev
->
phys
=
ts
->
phys
;
input_dev
->
id
.
bustype
=
BUS_SPI
;
input_dev
->
dev
.
parent
=
&
spi
->
dev
;
input_dev
->
evbit
[
0
]
=
BIT
(
EV_ABS
)
|
BIT
(
EV_KEY
);
input_dev
->
keybit
[
BIT_WORD
(
BTN_TOUCH
)]
=
BIT_MASK
(
BTN_TOUCH
);
input_set_abs_params
(
input_dev
,
ABS_X
,
0
,
max_x
,
fudge_x
,
0
);
input_set_abs_params
(
input_dev
,
ABS_Y
,
0
,
max_y
,
fudge_y
,
0
);
input_set_abs_params
(
input_dev
,
ABS_PRESSURE
,
0
,
max_p
,
fudge_p
,
0
);
input_dev
->
open
=
tsc2005_open
;
input_dev
->
close
=
tsc2005_close
;
input_set_drvdata
(
input_dev
,
ts
);
/* Ensure the touchscreen is off */
tsc2005_stop_scan
(
ts
);
error
=
request_threaded_irq
(
spi
->
irq
,
NULL
,
tsc2005_irq_thread
,
IRQF_TRIGGER_RISING
,
"tsc2005"
,
ts
);
if
(
error
)
{
dev_err
(
&
spi
->
dev
,
"Failed to request irq, err: %d
\n
"
,
error
);
goto
err_free_mem
;
}
spi_set_drvdata
(
spi
,
ts
);
error
=
sysfs_create_group
(
&
spi
->
dev
.
kobj
,
&
tsc2005_attr_group
);
if
(
error
)
{
dev_err
(
&
spi
->
dev
,
"Failed to create sysfs attributes, err: %d
\n
"
,
error
);
goto
err_clear_drvdata
;
}
error
=
input_register_device
(
ts
->
idev
);
if
(
error
)
{
dev_err
(
&
spi
->
dev
,
"Failed to register input device, err: %d
\n
"
,
error
);
goto
err_remove_sysfs
;
}
set_irq_wake
(
spi
->
irq
,
1
);
return
0
;
err_remove_sysfs:
sysfs_remove_group
(
&
spi
->
dev
.
kobj
,
&
tsc2005_attr_group
);
err_clear_drvdata:
spi_set_drvdata
(
spi
,
NULL
);
free_irq
(
spi
->
irq
,
ts
);
err_free_mem:
input_free_device
(
input_dev
);
kfree
(
ts
);
return
error
;
}
static
int
__devexit
tsc2005_remove
(
struct
spi_device
*
spi
)
{
struct
tsc2005
*
ts
=
spi_get_drvdata
(
spi
);
sysfs_remove_group
(
&
ts
->
spi
->
dev
.
kobj
,
&
tsc2005_attr_group
);
free_irq
(
ts
->
spi
->
irq
,
ts
);
input_unregister_device
(
ts
->
idev
);
kfree
(
ts
);
spi_set_drvdata
(
spi
,
NULL
);
return
0
;
}
#ifdef CONFIG_PM_SLEEP
static
int
tsc2005_suspend
(
struct
device
*
dev
)
{
struct
spi_device
*
spi
=
to_spi_device
(
dev
);
struct
tsc2005
*
ts
=
spi_get_drvdata
(
spi
);
mutex_lock
(
&
ts
->
mutex
);
if
(
!
ts
->
suspended
&&
ts
->
opened
)
__tsc2005_disable
(
ts
);
ts
->
suspended
=
true
;
mutex_unlock
(
&
ts
->
mutex
);
return
0
;
}
static
int
tsc2005_resume
(
struct
device
*
dev
)
{
struct
spi_device
*
spi
=
to_spi_device
(
dev
);
struct
tsc2005
*
ts
=
spi_get_drvdata
(
spi
);
mutex_lock
(
&
ts
->
mutex
);
if
(
ts
->
suspended
&&
ts
->
opened
)
__tsc2005_enable
(
ts
);
ts
->
suspended
=
false
;
mutex_unlock
(
&
ts
->
mutex
);
return
0
;
}
#endif
static
SIMPLE_DEV_PM_OPS
(
tsc2005_pm_ops
,
tsc2005_suspend
,
tsc2005_resume
);
static
struct
spi_driver
tsc2005_driver
=
{
.
driver
=
{
.
name
=
"tsc2005"
,
.
owner
=
THIS_MODULE
,
.
pm
=
&
tsc2005_pm_ops
,
},
.
probe
=
tsc2005_probe
,
.
remove
=
__devexit_p
(
tsc2005_remove
),
};
static
int
__init
tsc2005_init
(
void
)
{
return
spi_register_driver
(
&
tsc2005_driver
);
}
module_init
(
tsc2005_init
);
static
void
__exit
tsc2005_exit
(
void
)
{
spi_unregister_driver
(
&
tsc2005_driver
);
}
module_exit
(
tsc2005_exit
);
MODULE_AUTHOR
(
"Lauri Leukkunen <lauri.leukkunen@nokia.com>"
);
MODULE_DESCRIPTION
(
"TSC2005 Touchscreen Driver"
);
MODULE_LICENSE
(
"GPL"
);
include/linux/spi/tsc2005.h
0 → 100644
浏览文件 @
def179c2
/*
* This file is part of TSC2005 touchscreen driver
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Aaro Koskinen <aaro.koskinen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _LINUX_SPI_TSC2005_H
#define _LINUX_SPI_TSC2005_H
#include <linux/types.h>
struct
tsc2005_platform_data
{
int
ts_pressure_max
;
int
ts_pressure_fudge
;
int
ts_x_max
;
int
ts_x_fudge
;
int
ts_y_max
;
int
ts_y_fudge
;
int
ts_x_plate_ohm
;
unsigned
int
esd_timeout_ms
;
void
(
*
set_reset
)(
bool
enable
);
};
#endif
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录