2008-12-20 04:26:01 -05:00
/*
* drivers / input / touchscreen / tsc2007 . c
*
* Copyright ( c ) 2008 MtekVision Co . , Ltd .
* Kwangwoo Lee < kwlee @ mtekvision . com >
*
* Using code from :
* - ads7846 . c
* Copyright ( c ) 2005 David Brownell
* Copyright ( c ) 2006 Nokia Corporation
* - corgi_ts . c
* Copyright ( C ) 2004 - 2005 Richard Purdie
* - omap_ts . [ hc ] , ads7846 . h , ts_osk . c
* Copyright ( C ) 2002 MontaVista Software
* Copyright ( C ) 2004 Texas Instruments
* Copyright ( C ) 2005 Dirk Behme
*
* 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 .
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/i2c.h>
# include <linux/i2c/tsc2007.h>
2013-11-19 11:56:04 -08:00
# include <linux/of_device.h>
# include <linux/of.h>
# include <linux/of_gpio.h>
2008-12-20 04:26:01 -05:00
# define TSC2007_MEASURE_TEMP0 (0x0 << 4)
# define TSC2007_MEASURE_AUX (0x2 << 4)
# define TSC2007_MEASURE_TEMP1 (0x4 << 4)
# define TSC2007_ACTIVATE_XN (0x8 << 4)
# define TSC2007_ACTIVATE_YN (0x9 << 4)
# define TSC2007_ACTIVATE_YP_XN (0xa << 4)
# define TSC2007_SETUP (0xb << 4)
# define TSC2007_MEASURE_X (0xc << 4)
# define TSC2007_MEASURE_Y (0xd << 4)
# define TSC2007_MEASURE_Z1 (0xe << 4)
# define TSC2007_MEASURE_Z2 (0xf << 4)
# define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
# define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
# define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
# define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
# define TSC2007_12BIT (0x0 << 1)
# define TSC2007_8BIT (0x1 << 1)
# define MAX_12BIT ((1 << 12) - 1)
# define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
# define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
# define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
# define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
# define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
# define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
struct ts_event {
u16 x ;
u16 y ;
u16 z1 , z2 ;
} ;
struct tsc2007 {
struct input_dev * input ;
char phys [ 32 ] ;
struct i2c_client * client ;
u16 model ;
u16 x_plate_ohms ;
2011-05-17 09:31:01 -07:00
u16 max_rt ;
2015-03-31 14:35:36 -07:00
unsigned long poll_period ; /* in jiffies */
2013-11-19 11:56:04 -08:00
int fuzzx ;
int fuzzy ;
int fuzzz ;
2008-12-20 04:26:01 -05:00
2013-11-19 11:56:04 -08:00
unsigned gpio ;
2008-12-20 04:26:01 -05:00
int irq ;
2011-08-25 00:25:12 -07:00
wait_queue_head_t wait ;
bool stopped ;
2013-11-19 11:56:04 -08:00
int ( * get_pendown_state ) ( struct device * ) ;
2008-12-20 04:26:01 -05:00
void ( * clear_penirq ) ( void ) ;
} ;
static inline int tsc2007_xfer ( struct tsc2007 * tsc , u8 cmd )
{
s32 data ;
u16 val ;
data = i2c_smbus_read_word_data ( tsc - > client , cmd ) ;
if ( data < 0 ) {
dev_err ( & tsc - > client - > dev , " i2c io error: %d \n " , data ) ;
return data ;
}
/* The protocol and raw data format from i2c interface:
* S Addr Wr [ A ] Comm [ A ] S Addr Rd [ A ] [ DataLow ] A [ DataHigh ] NA P
* Where DataLow has [ D11 - D4 ] , DataHigh has [ D3 - D0 < < 4 | Dummy 4 bit ] .
*/
val = swab16 ( data ) > > 4 ;
dev_dbg ( & tsc - > client - > dev , " data: 0x%x, val: 0x%x \n " , data , val ) ;
return val ;
}
2009-08-04 22:07:26 -07:00
static void tsc2007_read_values ( struct tsc2007 * tsc , struct ts_event * tc )
2008-12-20 04:26:01 -05:00
{
2009-08-04 22:07:26 -07:00
/* y- still on; turn on only y+ (and ADC) */
tc - > y = tsc2007_xfer ( tsc , READ_Y ) ;
/* turn y- off, x+ on, then leave in lowpower */
tc - > x = tsc2007_xfer ( tsc , READ_X ) ;
/* turn y+ off, x- on; we'll use formula #1 */
tc - > z1 = tsc2007_xfer ( tsc , READ_Z1 ) ;
tc - > z2 = tsc2007_xfer ( tsc , READ_Z2 ) ;
/* Prepare for next touch reading - power down ADC, enable PENIRQ */
tsc2007_xfer ( tsc , PWRDOWN ) ;
}
2008-12-20 04:26:01 -05:00
2009-08-04 22:07:26 -07:00
static u32 tsc2007_calculate_pressure ( struct tsc2007 * tsc , struct ts_event * tc )
{
u32 rt = 0 ;
2008-12-20 04:26:01 -05:00
/* range filtering */
2009-08-04 22:07:26 -07:00
if ( tc - > x = = MAX_12BIT )
tc - > x = 0 ;
2008-12-20 04:26:01 -05:00
2009-08-04 22:07:26 -07:00
if ( likely ( tc - > x & & tc - > z1 ) ) {
2008-12-20 04:26:01 -05:00
/* compute touch pressure resistance using equation #1 */
2009-08-04 22:07:26 -07:00
rt = tc - > z2 - tc - > z1 ;
rt * = tc - > x ;
rt * = tsc - > x_plate_ohms ;
rt / = tc - > z1 ;
2008-12-20 04:26:01 -05:00
rt = ( rt + 2047 ) > > 12 ;
}
2009-08-04 22:07:26 -07:00
return rt ;
}
2011-08-25 00:25:12 -07:00
static bool tsc2007_is_pen_down ( struct tsc2007 * ts )
2009-08-04 22:07:26 -07:00
{
2009-07-24 23:14:16 -07:00
/*
* NOTE : We can ' t rely on the pressure to determine the pen down
2009-08-04 22:07:26 -07:00
* state , even though this controller has a pressure sensor .
* The pressure value can fluctuate for quite a while after
* lifting the pen and in some cases may not even settle at the
* expected value .
2008-12-20 04:26:01 -05:00
*
* The only safe way to check for the pen up condition is in the
2009-08-04 22:07:26 -07:00
* work function by reading the pen signal state ( it ' s a GPIO
* and IRQ ) . Unfortunately such callback is not always available ,
2011-08-25 00:25:12 -07:00
* in that case we assume that the pen is down and expect caller
* to fall back on the pressure reading .
2008-12-20 04:26:01 -05:00
*/
2009-08-04 22:07:26 -07:00
2011-08-25 00:25:12 -07:00
if ( ! ts - > get_pendown_state )
return true ;
2009-08-04 22:07:26 -07:00
2013-11-19 11:56:04 -08:00
return ts - > get_pendown_state ( & ts - > client - > dev ) ;
2011-08-25 00:25:12 -07:00
}
2009-08-04 22:07:26 -07:00
2011-08-25 00:25:12 -07:00
static irqreturn_t tsc2007_soft_irq ( int irq , void * handle )
{
struct tsc2007 * ts = handle ;
struct input_dev * input = ts - > input ;
struct ts_event tc ;
u32 rt ;
2009-08-04 22:07:26 -07:00
2011-08-25 00:25:12 -07:00
while ( ! ts - > stopped & & tsc2007_is_pen_down ( ts ) ) {
/* pen is down, continue with the measurement */
tsc2007_read_values ( ts , & tc ) ;
2009-08-04 22:07:26 -07:00
2011-08-25 00:25:12 -07:00
rt = tsc2007_calculate_pressure ( ts , & tc ) ;
2008-12-20 04:26:01 -05:00
2013-11-19 11:56:04 -08:00
if ( ! rt & & ! ts - > get_pendown_state ) {
2011-08-25 00:25:12 -07:00
/*
* If pressure reported is 0 and we don ' t have
* callback to check pendown state , we have to
* assume that pen was lifted up .
*/
break ;
}
if ( rt < = ts - > max_rt ) {
dev_dbg ( & ts - > client - > dev ,
" DOWN point(%4d,%4d), pressure (%4u) \n " ,
tc . x , tc . y , rt ) ;
2008-12-20 04:26:01 -05:00
input_report_key ( input , BTN_TOUCH , 1 ) ;
2011-08-25 00:25:12 -07:00
input_report_abs ( input , ABS_X , tc . x ) ;
input_report_abs ( input , ABS_Y , tc . y ) ;
input_report_abs ( input , ABS_PRESSURE , rt ) ;
input_sync ( input ) ;
} else {
/*
* Sample found inconsistent by debouncing or pressure is
* beyond the maximum . Don ' t report it to user space ,
* repeat at least once more the measurement .
*/
dev_dbg ( & ts - > client - > dev , " ignored pressure %d \n " , rt ) ;
2008-12-20 04:26:01 -05:00
}
2015-03-31 14:35:36 -07:00
wait_event_timeout ( ts - > wait , ts - > stopped , ts - > poll_period ) ;
2011-08-25 00:25:12 -07:00
}
2008-12-20 04:26:01 -05:00
2011-08-25 00:25:12 -07:00
dev_dbg ( & ts - > client - > dev , " UP \n " ) ;
2008-12-20 04:26:01 -05:00
2011-08-25 00:25:12 -07:00
input_report_key ( input , BTN_TOUCH , 0 ) ;
input_report_abs ( input , ABS_PRESSURE , 0 ) ;
input_sync ( input ) ;
2008-12-20 04:26:01 -05:00
2011-08-25 00:25:12 -07:00
if ( ts - > clear_penirq )
ts - > clear_penirq ( ) ;
2009-07-24 23:14:16 -07:00
2011-08-25 00:25:12 -07:00
return IRQ_HANDLED ;
2008-12-20 04:26:01 -05:00
}
2011-08-25 00:25:12 -07:00
static irqreturn_t tsc2007_hard_irq ( int irq , void * handle )
2008-12-20 04:26:01 -05:00
{
struct tsc2007 * ts = handle ;
2013-11-19 11:56:04 -08:00
if ( tsc2007_is_pen_down ( ts ) )
2011-08-25 00:25:12 -07:00
return IRQ_WAKE_THREAD ;
2008-12-20 04:26:01 -05:00
if ( ts - > clear_penirq )
ts - > clear_penirq ( ) ;
return IRQ_HANDLED ;
}
2011-08-25 01:05:46 -07:00
static void tsc2007_stop ( struct tsc2007 * ts )
2009-07-24 23:14:16 -07:00
{
2011-08-25 00:25:12 -07:00
ts - > stopped = true ;
mb ( ) ;
wake_up ( & ts - > wait ) ;
2011-08-25 01:05:46 -07:00
disable_irq ( ts - > irq ) ;
}
static int tsc2007_open ( struct input_dev * input_dev )
{
struct tsc2007 * ts = input_get_drvdata ( input_dev ) ;
int err ;
ts - > stopped = false ;
mb ( ) ;
enable_irq ( ts - > irq ) ;
/* Prepare for touch readings - power down ADC and enable PENIRQ */
err = tsc2007_xfer ( ts , PWRDOWN ) ;
if ( err < 0 ) {
tsc2007_stop ( ts ) ;
return err ;
}
return 0 ;
}
static void tsc2007_close ( struct input_dev * input_dev )
{
struct tsc2007 * ts = input_get_drvdata ( input_dev ) ;
tsc2007_stop ( ts ) ;
2009-07-24 23:14:16 -07:00
}
2013-11-19 11:56:04 -08:00
# ifdef CONFIG_OF
static int tsc2007_get_pendown_state_gpio ( struct device * dev )
2008-12-20 04:26:01 -05:00
{
2013-11-19 11:56:04 -08:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct tsc2007 * ts = i2c_get_clientdata ( client ) ;
return ! gpio_get_value ( ts - > gpio ) ;
}
static int tsc2007_probe_dt ( struct i2c_client * client , struct tsc2007 * ts )
{
struct device_node * np = client - > dev . of_node ;
u32 val32 ;
u64 val64 ;
2008-12-20 04:26:01 -05:00
2013-11-19 11:56:04 -08:00
if ( ! np ) {
dev_err ( & client - > dev , " missing device tree data \n " ) ;
2008-12-20 04:26:01 -05:00
return - EINVAL ;
}
2013-11-19 11:56:04 -08:00
if ( ! of_property_read_u32 ( np , " ti,max-rt " , & val32 ) )
ts - > max_rt = val32 ;
else
ts - > max_rt = MAX_12BIT ;
2008-12-20 04:26:01 -05:00
2013-11-19 11:56:04 -08:00
if ( ! of_property_read_u32 ( np , " ti,fuzzx " , & val32 ) )
ts - > fuzzx = val32 ;
if ( ! of_property_read_u32 ( np , " ti,fuzzy " , & val32 ) )
ts - > fuzzy = val32 ;
if ( ! of_property_read_u32 ( np , " ti,fuzzz " , & val32 ) )
ts - > fuzzz = val32 ;
if ( ! of_property_read_u64 ( np , " ti,poll-period " , & val64 ) )
2015-03-31 14:35:36 -07:00
ts - > poll_period = msecs_to_jiffies ( val64 ) ;
2013-11-19 11:56:04 -08:00
else
2015-03-31 14:35:36 -07:00
ts - > poll_period = msecs_to_jiffies ( 1 ) ;
2013-11-19 11:56:04 -08:00
if ( ! of_property_read_u32 ( np , " ti,x-plate-ohms " , & val32 ) ) {
ts - > x_plate_ohms = val32 ;
} else {
dev_err ( & client - > dev , " missing ti,x-plate-ohms devicetree property. " ) ;
return - EINVAL ;
2008-12-20 04:26:01 -05:00
}
2013-11-19 11:56:04 -08:00
ts - > gpio = of_get_gpio ( np , 0 ) ;
if ( gpio_is_valid ( ts - > gpio ) )
ts - > get_pendown_state = tsc2007_get_pendown_state_gpio ;
else
dev_warn ( & client - > dev ,
" GPIO not specified in DT (of_get_gpio returned %d) \n " ,
ts - > gpio ) ;
2008-12-20 04:26:01 -05:00
2013-11-19 11:56:04 -08:00
return 0 ;
}
# else
static int tsc2007_probe_dt ( struct i2c_client * client , struct tsc2007 * ts )
{
dev_err ( & client - > dev , " platform data is required! \n " ) ;
return - EINVAL ;
}
# endif
static int tsc2007_probe_pdev ( struct i2c_client * client , struct tsc2007 * ts ,
const struct tsc2007_platform_data * pdata ,
const struct i2c_device_id * id )
{
2008-12-20 04:26:01 -05:00
ts - > model = pdata - > model ;
ts - > x_plate_ohms = pdata - > x_plate_ohms ;
2011-05-17 09:31:01 -07:00
ts - > max_rt = pdata - > max_rt ? : MAX_12BIT ;
2015-03-31 14:35:36 -07:00
ts - > poll_period = msecs_to_jiffies ( pdata - > poll_period ? : 1 ) ;
2008-12-20 04:26:01 -05:00
ts - > get_pendown_state = pdata - > get_pendown_state ;
ts - > clear_penirq = pdata - > clear_penirq ;
2013-11-19 11:56:04 -08:00
ts - > fuzzx = pdata - > fuzzx ;
ts - > fuzzy = pdata - > fuzzy ;
ts - > fuzzz = pdata - > fuzzz ;
2008-12-20 04:26:01 -05:00
2011-10-11 20:54:55 -07:00
if ( pdata - > x_plate_ohms = = 0 ) {
dev_err ( & client - > dev , " x_plate_ohms is not set up in platform data " ) ;
2013-11-19 11:56:04 -08:00
return - EINVAL ;
2011-10-11 20:54:55 -07:00
}
2013-11-19 11:56:04 -08:00
return 0 ;
}
2013-11-19 12:52:29 -08:00
static void tsc2007_call_exit_platform_hw ( void * data )
{
struct device * dev = data ;
const struct tsc2007_platform_data * pdata = dev_get_platdata ( dev ) ;
pdata - > exit_platform_hw ( ) ;
}
2013-11-19 11:56:04 -08:00
static int tsc2007_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
const struct tsc2007_platform_data * pdata = dev_get_platdata ( & client - > dev ) ;
struct tsc2007 * ts ;
struct input_dev * input_dev ;
int err ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_WORD_DATA ) )
return - EIO ;
ts = devm_kzalloc ( & client - > dev , sizeof ( struct tsc2007 ) , GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
if ( pdata )
err = tsc2007_probe_pdev ( client , ts , pdata , id ) ;
else
err = tsc2007_probe_dt ( client , ts ) ;
if ( err )
return err ;
2013-11-19 12:52:29 -08:00
input_dev = devm_input_allocate_device ( & client - > dev ) ;
if ( ! input_dev )
return - ENOMEM ;
2013-11-19 11:56:04 -08:00
i2c_set_clientdata ( client , ts ) ;
ts - > client = client ;
ts - > irq = client - > irq ;
ts - > input = input_dev ;
init_waitqueue_head ( & ts - > wait ) ;
2009-01-29 22:56:08 -08:00
snprintf ( ts - > phys , sizeof ( ts - > phys ) ,
" %s/input0 " , dev_name ( & client - > dev ) ) ;
2008-12-20 04:26:01 -05:00
input_dev - > name = " TSC2007 Touchscreen " ;
input_dev - > phys = ts - > phys ;
input_dev - > id . bustype = BUS_I2C ;
2011-08-25 01:05:46 -07:00
input_dev - > open = tsc2007_open ;
input_dev - > close = tsc2007_close ;
input_set_drvdata ( input_dev , ts ) ;
2008-12-20 04:26:01 -05:00
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_ABS ) ;
input_dev - > keybit [ BIT_WORD ( BTN_TOUCH ) ] = BIT_MASK ( BTN_TOUCH ) ;
2013-11-19 11:56:04 -08:00
input_set_abs_params ( input_dev , ABS_X , 0 , MAX_12BIT , ts - > fuzzx , 0 ) ;
input_set_abs_params ( input_dev , ABS_Y , 0 , MAX_12BIT , ts - > fuzzy , 0 ) ;
2011-05-17 09:32:29 -07:00
input_set_abs_params ( input_dev , ABS_PRESSURE , 0 , MAX_12BIT ,
2013-11-19 11:56:04 -08:00
ts - > fuzzz , 0 ) ;
2008-12-20 04:26:01 -05:00
2013-11-19 12:52:29 -08:00
if ( pdata ) {
if ( pdata - > exit_platform_hw ) {
err = devm_add_action ( & client - > dev ,
tsc2007_call_exit_platform_hw ,
& client - > dev ) ;
if ( err ) {
dev_err ( & client - > dev ,
" Failed to register exit_platform_hw action, %d \n " ,
err ) ;
return err ;
}
}
if ( pdata - > init_platform_hw )
pdata - > init_platform_hw ( ) ;
}
2009-07-24 22:01:39 -07:00
2013-11-19 12:52:29 -08:00
err = devm_request_threaded_irq ( & client - > dev , ts - > irq ,
tsc2007_hard_irq , tsc2007_soft_irq ,
IRQF_ONESHOT ,
client - > dev . driver - > name , ts ) ;
if ( err ) {
dev_err ( & client - > dev , " Failed to request irq %d: %d \n " ,
ts - > irq , err ) ;
return err ;
2008-12-20 04:26:01 -05:00
}
2011-08-25 01:05:46 -07:00
tsc2007_stop ( ts ) ;
2009-08-04 22:34:10 -07:00
2008-12-20 04:26:01 -05:00
err = input_register_device ( input_dev ) ;
2013-11-19 12:52:29 -08:00
if ( err ) {
dev_err ( & client - > dev ,
" Failed to register input device: %d \n " , err ) ;
return err ;
}
2008-12-20 04:26:01 -05:00
return 0 ;
}
2010-01-09 23:23:02 -08:00
static const struct i2c_device_id tsc2007_idtable [ ] = {
2008-12-20 04:26:01 -05:00
{ " tsc2007 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tsc2007_idtable ) ;
2013-11-19 11:56:04 -08:00
# ifdef CONFIG_OF
static const struct of_device_id tsc2007_of_match [ ] = {
{ . compatible = " ti,tsc2007 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , tsc2007_of_match ) ;
# endif
2008-12-20 04:26:01 -05:00
static struct i2c_driver tsc2007_driver = {
. driver = {
2013-11-19 11:56:04 -08:00
. name = " tsc2007 " ,
. of_match_table = of_match_ptr ( tsc2007_of_match ) ,
2008-12-20 04:26:01 -05:00
} ,
. id_table = tsc2007_idtable ,
. probe = tsc2007_probe ,
} ;
2012-03-16 23:05:41 -07:00
module_i2c_driver ( tsc2007_driver ) ;
2008-12-20 04:26:01 -05:00
MODULE_AUTHOR ( " Kwangwoo Lee <kwlee@mtekvision.com> " ) ;
MODULE_DESCRIPTION ( " TSC2007 TouchScreen Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;