2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
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
*/
# include <linux/module.h>
# include <linux/slab.h>
2021-03-15 16:49:53 -07:00
# include <linux/gpio/consumer.h>
2008-12-20 04:26:01 -05:00
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/i2c.h>
2021-03-15 17:00:57 -07:00
# include <linux/mod_devicetable.h>
# include <linux/property.h>
2017-05-22 16:30:04 -07:00
# include <linux/platform_data/tsc2007.h>
2017-02-17 12:53:32 -08:00
# include "tsc2007.h"
2008-12-20 04:26:01 -05:00
2017-02-17 12:53:32 -08:00
int tsc2007_xfer ( struct tsc2007 * tsc , u8 cmd )
2008-12-20 04:26:01 -05:00
{
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
2017-02-22 23:49:02 -08:00
u32 tsc2007_calculate_resistance ( struct tsc2007 * tsc , struct ts_event * tc )
2009-08-04 22:07:26 -07:00
{
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 ) ) {
2017-02-22 23:49:02 -08:00
/* compute touch 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 ;
}
2017-02-17 12:53:32 -08:00
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 */
2017-02-17 12:53:32 -08:00
mutex_lock ( & ts - > mlock ) ;
2011-08-25 00:25:12 -07:00
tsc2007_read_values ( ts , & tc ) ;
2017-02-17 12:53:32 -08:00
mutex_unlock ( & ts - > mlock ) ;
2009-08-04 22:07:26 -07:00
2017-02-22 23:49:02 -08:00
rt = tsc2007_calculate_resistance ( 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 ,
2017-02-22 23:49:02 -08:00
" DOWN point(%4d,%4d), resistance (%4u) \n " ,
2011-08-25 00:25:12 -07:00
tc . x , tc . y , rt ) ;
2008-12-20 04:26:01 -05:00
2017-02-22 23:53:02 -08:00
rt = ts - > max_rt - 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
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 ) ;
2021-03-15 16:49:53 -07:00
return gpiod_get_value ( ts - > gpiod ) ;
2013-11-19 11:56:04 -08:00
}
2021-03-15 17:00:57 -07:00
static int tsc2007_probe_properties ( struct device * dev , struct tsc2007 * ts )
2013-11-19 11:56:04 -08:00
{
u32 val32 ;
u64 val64 ;
2008-12-20 04:26:01 -05:00
2021-03-15 17:00:57 -07:00
if ( ! device_property_read_u32 ( dev , " ti,max-rt " , & val32 ) )
2013-11-19 11:56:04 -08:00
ts - > max_rt = val32 ;
else
ts - > max_rt = MAX_12BIT ;
2008-12-20 04:26:01 -05:00
2021-03-15 17:00:57 -07:00
if ( ! device_property_read_u32 ( dev , " ti,fuzzx " , & val32 ) )
2013-11-19 11:56:04 -08:00
ts - > fuzzx = val32 ;
2021-03-15 17:00:57 -07:00
if ( ! device_property_read_u32 ( dev , " ti,fuzzy " , & val32 ) )
2013-11-19 11:56:04 -08:00
ts - > fuzzy = val32 ;
2021-03-15 17:00:57 -07:00
if ( ! device_property_read_u32 ( dev , " ti,fuzzz " , & val32 ) )
2013-11-19 11:56:04 -08:00
ts - > fuzzz = val32 ;
2021-03-15 17:00:57 -07:00
if ( ! device_property_read_u64 ( dev , " 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
2021-03-15 17:00:57 -07:00
if ( ! device_property_read_u32 ( dev , " ti,x-plate-ohms " , & val32 ) ) {
2013-11-19 11:56:04 -08:00
ts - > x_plate_ohms = val32 ;
} else {
2021-03-15 17:00:57 -07:00
dev_err ( dev , " Missing ti,x-plate-ohms device property \n " ) ;
2013-11-19 11:56:04 -08:00
return - EINVAL ;
2008-12-20 04:26:01 -05:00
}
2021-03-15 17:00:57 -07:00
ts - > gpiod = devm_gpiod_get_optional ( dev , NULL , GPIOD_IN ) ;
2021-03-15 16:49:53 -07:00
if ( IS_ERR ( ts - > gpiod ) )
return PTR_ERR ( ts - > gpiod ) ;
if ( ts - > gpiod )
2013-11-19 11:56:04 -08:00
ts - > get_pendown_state = tsc2007_get_pendown_state_gpio ;
else
2021-03-15 17:00:57 -07:00
dev_warn ( dev , " Pen down GPIO is not specified in properties \n " ) ;
2008-12-20 04:26:01 -05:00
2013-11-19 11:56:04 -08:00
return 0 ;
}
2021-03-15 17:00:57 -07:00
static int tsc2007_probe_pdev ( struct device * dev , struct tsc2007 * ts ,
2013-11-19 11:56:04 -08:00
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 ) {
2021-03-15 17:00:57 -07:00
dev_err ( dev , " x_plate_ohms is not set up in platform data \n " ) ;
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 ( ) ;
}
2022-11-18 23:39:57 +01:00
static int tsc2007_probe ( struct i2c_client * client )
2013-11-19 11:56:04 -08:00
{
2022-11-18 23:39:57 +01:00
const struct i2c_device_id * id = i2c_client_get_device_id ( client ) ;
2017-02-17 12:53:32 -08:00
const struct tsc2007_platform_data * pdata =
dev_get_platdata ( & client - > dev ) ;
2013-11-19 11:56:04 -08:00
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 )
2021-03-15 17:00:57 -07:00
err = tsc2007_probe_pdev ( & client - > dev , ts , pdata , id ) ;
2013-11-19 11:56:04 -08:00
else
2021-03-15 17:00:57 -07:00
err = tsc2007_probe_properties ( & client - > dev , ts ) ;
2013-11-19 11:56:04 -08:00
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 ;
2017-02-17 12:53:32 -08:00
2013-11-19 11:56:04 -08:00
init_waitqueue_head ( & ts - > wait ) ;
2017-02-17 12:53:32 -08:00
mutex_init ( & ts - > mlock ) ;
2013-11-19 11:56:04 -08:00
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 ) ;
2017-02-17 14:51:13 -08:00
input_set_capability ( input_dev , EV_KEY , BTN_TOUCH ) ;
2008-12-20 04:26:01 -05:00
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
2017-02-17 12:51:19 -08:00
/* power down the chip (TSC2007_SETUP does not ACK on I2C) */
err = tsc2007_xfer ( ts , PWRDOWN ) ;
if ( err < 0 ) {
dev_err ( & client - > dev ,
" Failed to setup chip: %d \n " , err ) ;
2017-02-17 12:53:32 -08:00
return err ; /* chip does not respond */
2017-02-17 12:51:19 -08: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
2017-02-17 12:53:32 -08:00
err = tsc2007_iio_configure ( ts ) ;
if ( err ) {
dev_err ( & client - > dev ,
" Failed to register with IIO: %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
static const struct of_device_id tsc2007_of_match [ ] = {
{ . compatible = " ti,tsc2007 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , tsc2007_of_match ) ;
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 " ,
2021-03-15 17:00:57 -07:00
. of_match_table = tsc2007_of_match ,
2008-12-20 04:26:01 -05:00
} ,
. id_table = tsc2007_idtable ,
2022-11-18 23:39:57 +01:00
. probe_new = tsc2007_probe ,
2008-12-20 04:26:01 -05:00
} ;
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 " ) ;