2019-05-28 09:57:05 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2010-07-30 09:02:43 -07:00
/*
* Driver for cypress touch screen controller
*
* Copyright ( c ) 2009 Aava Mobile
*
* Some cleanups by Alan Cox < alan @ linux . intel . com >
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/input.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/i2c.h>
# include <linux/gpio.h>
# include <linux/input/cy8ctmg110_pdata.h>
2021-06-05 17:05:29 -07:00
# include <asm/byteorder.h>
2010-07-30 09:02:43 -07:00
# define CY8CTMG110_DRIVER_NAME "cy8ctmg110"
/* Touch coordinates */
# define CY8CTMG110_X_MIN 0
# define CY8CTMG110_Y_MIN 0
# define CY8CTMG110_X_MAX 759
# define CY8CTMG110_Y_MAX 465
/* cy8ctmg110 register definitions */
# define CY8CTMG110_TOUCH_WAKEUP_TIME 0
# define CY8CTMG110_TOUCH_SLEEP_TIME 2
# define CY8CTMG110_TOUCH_X1 3
# define CY8CTMG110_TOUCH_Y1 5
# define CY8CTMG110_TOUCH_X2 7
# define CY8CTMG110_TOUCH_Y2 9
# define CY8CTMG110_FINGERS 11
# define CY8CTMG110_GESTURE 12
# define CY8CTMG110_REG_MAX 13
/*
* The touch driver structure .
*/
struct cy8ctmg110 {
struct input_dev * input ;
char phys [ 32 ] ;
struct i2c_client * client ;
int reset_pin ;
} ;
/*
* cy8ctmg110_power is the routine that is called when touch hardware
* will powered off or on .
*/
static void cy8ctmg110_power ( struct cy8ctmg110 * ts , bool poweron )
{
if ( ts - > reset_pin )
gpio_direction_output ( ts - > reset_pin , 1 - poweron ) ;
}
static int cy8ctmg110_write_regs ( struct cy8ctmg110 * tsc , unsigned char reg ,
unsigned char len , unsigned char * value )
{
struct i2c_client * client = tsc - > client ;
2010-08-09 10:06:47 -07:00
int ret ;
2010-07-30 09:02:43 -07:00
unsigned char i2c_data [ 6 ] ;
BUG_ON ( len > 5 ) ;
i2c_data [ 0 ] = reg ;
memcpy ( i2c_data + 1 , value , len ) ;
ret = i2c_master_send ( client , i2c_data , len + 1 ) ;
2011-07-13 00:03:36 -07:00
if ( ret ! = len + 1 ) {
2010-07-30 09:02:43 -07:00
dev_err ( & client - > dev , " i2c write data cmd failed \n " ) ;
2011-07-13 00:03:36 -07:00
return ret < 0 ? ret : - EIO ;
2010-07-30 09:02:43 -07:00
}
return 0 ;
}
static int cy8ctmg110_read_regs ( struct cy8ctmg110 * tsc ,
unsigned char * data , unsigned char len , unsigned char cmd )
{
struct i2c_client * client = tsc - > client ;
2010-08-09 10:06:47 -07:00
int ret ;
2010-07-30 09:02:43 -07:00
struct i2c_msg msg [ 2 ] = {
/* first write slave position to i2c devices */
2012-10-10 09:34:00 -07:00
{
. addr = client - > addr ,
. len = 1 ,
. buf = & cmd
} ,
2010-07-30 09:02:43 -07:00
/* Second read data from position */
2012-10-10 09:34:00 -07:00
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = len ,
. buf = data
}
2010-07-30 09:02:43 -07:00
} ;
ret = i2c_transfer ( client - > adapter , msg , 2 ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int cy8ctmg110_touch_pos ( struct cy8ctmg110 * tsc )
{
struct input_dev * input = tsc - > input ;
unsigned char reg_p [ CY8CTMG110_REG_MAX ] ;
memset ( reg_p , 0 , CY8CTMG110_REG_MAX ) ;
/* Reading coordinates */
if ( cy8ctmg110_read_regs ( tsc , reg_p , 9 , CY8CTMG110_TOUCH_X1 ) ! = 0 )
return - EIO ;
/* Number of touch */
if ( reg_p [ 8 ] = = 0 ) {
input_report_key ( input , BTN_TOUCH , 0 ) ;
} else {
input_report_key ( input , BTN_TOUCH , 1 ) ;
2021-06-05 17:05:29 -07:00
input_report_abs ( input , ABS_X ,
be16_to_cpup ( ( __be16 * ) ( reg_p + 0 ) ) ) ;
input_report_abs ( input , ABS_Y ,
be16_to_cpup ( ( __be16 * ) ( reg_p + 2 ) ) ) ;
2010-07-30 09:02:43 -07:00
}
input_sync ( input ) ;
return 0 ;
}
static int cy8ctmg110_set_sleepmode ( struct cy8ctmg110 * ts , bool sleep )
{
unsigned char reg_p [ 3 ] ;
if ( sleep ) {
reg_p [ 0 ] = 0x00 ;
reg_p [ 1 ] = 0xff ;
reg_p [ 2 ] = 5 ;
} else {
reg_p [ 0 ] = 0x10 ;
reg_p [ 1 ] = 0xff ;
reg_p [ 2 ] = 0 ;
}
return cy8ctmg110_write_regs ( ts , CY8CTMG110_TOUCH_WAKEUP_TIME , 3 , reg_p ) ;
}
static irqreturn_t cy8ctmg110_irq_thread ( int irq , void * dev_id )
{
struct cy8ctmg110 * tsc = dev_id ;
cy8ctmg110_touch_pos ( tsc ) ;
return IRQ_HANDLED ;
}
2012-11-23 21:38:25 -08:00
static int cy8ctmg110_probe ( struct i2c_client * client ,
2010-07-30 09:02:43 -07:00
const struct i2c_device_id * id )
{
2013-12-05 19:21:10 -08:00
const struct cy8ctmg110_pdata * pdata = dev_get_platdata ( & client - > dev ) ;
2010-07-30 09:02:43 -07:00
struct cy8ctmg110 * ts ;
struct input_dev * input_dev ;
int err ;
/* No pdata no way forward */
if ( pdata = = NULL ) {
dev_err ( & client - > dev , " no pdata \n " ) ;
return - ENODEV ;
}
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_WORD_DATA ) )
return - EIO ;
ts = kzalloc ( sizeof ( struct cy8ctmg110 ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! ts | | ! input_dev ) {
err = - ENOMEM ;
goto err_free_mem ;
}
ts - > client = client ;
ts - > input = input_dev ;
2011-07-18 17:09:37 -07:00
ts - > reset_pin = pdata - > reset_pin ;
2010-07-30 09:02:43 -07:00
snprintf ( ts - > phys , sizeof ( ts - > phys ) ,
" %s/input0 " , dev_name ( & client - > dev ) ) ;
input_dev - > name = CY8CTMG110_DRIVER_NAME " Touchscreen " ;
input_dev - > phys = ts - > phys ;
input_dev - > id . bustype = BUS_I2C ;
input_dev - > dev . parent = & client - > dev ;
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_ABS ) ;
input_dev - > keybit [ BIT_WORD ( BTN_TOUCH ) ] = BIT_MASK ( BTN_TOUCH ) ;
input_set_abs_params ( input_dev , ABS_X ,
2010-09-06 12:49:33 -07:00
CY8CTMG110_X_MIN , CY8CTMG110_X_MAX , 4 , 0 ) ;
2010-07-30 09:02:43 -07:00
input_set_abs_params ( input_dev , ABS_Y ,
2010-09-06 12:49:33 -07:00
CY8CTMG110_Y_MIN , CY8CTMG110_Y_MAX , 4 , 0 ) ;
2010-07-30 09:02:43 -07:00
if ( ts - > reset_pin ) {
err = gpio_request ( ts - > reset_pin , NULL ) ;
if ( err ) {
dev_err ( & client - > dev ,
" Unable to request GPIO pin %d. \n " ,
ts - > reset_pin ) ;
goto err_free_mem ;
}
}
cy8ctmg110_power ( ts , true ) ;
cy8ctmg110_set_sleepmode ( ts , false ) ;
err = request_threaded_irq ( client - > irq , NULL , cy8ctmg110_irq_thread ,
2021-06-05 17:04:39 -07:00
IRQF_ONESHOT , " touch_reset_key " , ts ) ;
2010-07-30 09:02:43 -07:00
if ( err < 0 ) {
dev_err ( & client - > dev ,
" irq %d busy? error %d \n " , client - > irq , err ) ;
2021-06-05 17:04:25 -07:00
goto err_shutoff_device ;
2010-07-30 09:02:43 -07:00
}
err = input_register_device ( input_dev ) ;
if ( err )
goto err_free_irq ;
i2c_set_clientdata ( client , ts ) ;
2021-06-05 17:04:49 -07:00
2010-07-30 09:02:43 -07:00
return 0 ;
err_free_irq :
free_irq ( client - > irq , ts ) ;
err_shutoff_device :
cy8ctmg110_set_sleepmode ( ts , true ) ;
cy8ctmg110_power ( ts , false ) ;
if ( ts - > reset_pin )
gpio_free ( ts - > reset_pin ) ;
err_free_mem :
input_free_device ( input_dev ) ;
kfree ( ts ) ;
return err ;
}
2014-11-02 00:04:14 -07:00
static int __maybe_unused cy8ctmg110_suspend ( struct device * dev )
2010-07-30 09:02:43 -07:00
{
2011-01-06 23:01:03 -08:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2010-07-30 09:02:43 -07:00
struct cy8ctmg110 * ts = i2c_get_clientdata ( client ) ;
2021-06-05 17:05:00 -07:00
if ( ! device_may_wakeup ( & client - > dev ) ) {
2010-07-30 09:02:43 -07:00
cy8ctmg110_set_sleepmode ( ts , true ) ;
cy8ctmg110_power ( ts , false ) ;
}
2021-06-05 17:05:00 -07:00
2010-07-30 09:02:43 -07:00
return 0 ;
}
2014-11-02 00:04:14 -07:00
static int __maybe_unused cy8ctmg110_resume ( struct device * dev )
2010-07-30 09:02:43 -07:00
{
2011-01-06 23:01:03 -08:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2010-07-30 09:02:43 -07:00
struct cy8ctmg110 * ts = i2c_get_clientdata ( client ) ;
2021-06-05 17:05:00 -07:00
if ( ! device_may_wakeup ( & client - > dev ) ) {
2010-07-30 09:02:43 -07:00
cy8ctmg110_power ( ts , true ) ;
cy8ctmg110_set_sleepmode ( ts , false ) ;
}
2021-06-05 17:05:00 -07:00
2010-07-30 09:02:43 -07:00
return 0 ;
}
2011-01-06 23:01:03 -08:00
static SIMPLE_DEV_PM_OPS ( cy8ctmg110_pm , cy8ctmg110_suspend , cy8ctmg110_resume ) ;
2010-07-30 09:02:43 -07:00
2012-11-23 21:50:47 -08:00
static int cy8ctmg110_remove ( struct i2c_client * client )
2010-07-30 09:02:43 -07:00
{
struct cy8ctmg110 * ts = i2c_get_clientdata ( client ) ;
cy8ctmg110_set_sleepmode ( ts , true ) ;
cy8ctmg110_power ( ts , false ) ;
free_irq ( client - > irq , ts ) ;
input_unregister_device ( ts - > input ) ;
if ( ts - > reset_pin )
gpio_free ( ts - > reset_pin ) ;
kfree ( ts ) ;
return 0 ;
}
2011-07-13 00:04:36 -07:00
static const struct i2c_device_id cy8ctmg110_idtable [ ] = {
2010-07-30 09:02:43 -07:00
{ CY8CTMG110_DRIVER_NAME , 1 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , cy8ctmg110_idtable ) ;
static struct i2c_driver cy8ctmg110_driver = {
. driver = {
. name = CY8CTMG110_DRIVER_NAME ,
2011-01-06 23:01:03 -08:00
. pm = & cy8ctmg110_pm ,
2010-07-30 09:02:43 -07:00
} ,
. id_table = cy8ctmg110_idtable ,
. probe = cy8ctmg110_probe ,
2012-11-23 21:27:39 -08:00
. remove = cy8ctmg110_remove ,
2010-07-30 09:02:43 -07:00
} ;
2012-03-16 23:05:41 -07:00
module_i2c_driver ( cy8ctmg110_driver ) ;
2010-07-30 09:02:43 -07:00
MODULE_AUTHOR ( " Samuli Konttila <samuli.konttila@aavamobile.com> " ) ;
MODULE_DESCRIPTION ( " cy8ctmg110 TouchScreen Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;