2013-01-09 16:25:11 -08:00
/*
* Cypress APA trackpad with I2C interface
*
* Author : Dudley Du < dudl @ cypress . com >
* Further cleanup and restructuring by :
* Daniel Kurtz < djkurtz @ chromium . org >
* Benson Leung < bleung @ chromium . org >
*
2015-07-20 16:49:06 -07:00
* Copyright ( C ) 2011 - 2015 Cypress Semiconductor , Inc .
2013-01-09 16:25:11 -08:00
* Copyright ( C ) 2011 - 2012 Google , Inc .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive for
* more details .
*/
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/input.h>
# include <linux/input/mt.h>
# include <linux/interrupt.h>
# include <linux/module.h>
2015-01-17 18:35:26 -08:00
# include <linux/mutex.h>
2015-07-30 11:19:18 -07:00
# include <linux/regulator/consumer.h>
2013-01-09 16:25:11 -08:00
# include <linux/slab.h>
2015-01-17 18:35:26 -08:00
# include <linux/uaccess.h>
2015-01-17 18:57:42 -08:00
# include <linux/pm_runtime.h>
2015-01-17 22:18:59 -08:00
# include <linux/acpi.h>
2015-07-30 11:24:16 -07:00
# include <linux/of.h>
2015-01-17 18:35:26 -08:00
# include "cyapa.h"
2013-01-09 16:25:11 -08:00
2013-02-13 13:56:03 -08:00
# define CYAPA_ADAPTER_FUNC_NONE 0
# define CYAPA_ADAPTER_FUNC_I2C 1
# define CYAPA_ADAPTER_FUNC_SMBUS 2
# define CYAPA_ADAPTER_FUNC_BOTH 3
2015-01-17 22:07:12 -08:00
# define CYAPA_FW_NAME "cyapa.bin"
2015-01-17 18:35:26 -08:00
const char product_id [ ] = " CYTRA " ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
static int cyapa_reinitialize ( struct cyapa * cyapa ) ;
2013-02-13 13:56:03 -08:00
2015-07-20 16:49:06 -07:00
bool cyapa_is_pip_bl_mode ( struct cyapa * cyapa )
2013-01-09 16:25:11 -08:00
{
2015-07-20 16:53:30 -07:00
if ( cyapa - > gen = = CYAPA_GEN6 & & cyapa - > state = = CYAPA_STATE_GEN6_BL )
return true ;
2015-01-17 18:35:26 -08:00
if ( cyapa - > gen = = CYAPA_GEN5 & & cyapa - > state = = CYAPA_STATE_GEN5_BL )
return true ;
2015-07-20 16:49:06 -07:00
return false ;
}
bool cyapa_is_pip_app_mode ( struct cyapa * cyapa )
{
2015-07-20 16:53:30 -07:00
if ( cyapa - > gen = = CYAPA_GEN6 & & cyapa - > state = = CYAPA_STATE_GEN6_APP )
return true ;
2015-07-20 16:49:06 -07:00
if ( cyapa - > gen = = CYAPA_GEN5 & & cyapa - > state = = CYAPA_STATE_GEN5_APP )
return true ;
return false ;
}
static bool cyapa_is_bootloader_mode ( struct cyapa * cyapa )
{
if ( cyapa_is_pip_bl_mode ( cyapa ) )
return true ;
2015-01-17 18:35:26 -08:00
if ( cyapa - > gen = = CYAPA_GEN3 & &
cyapa - > state > = CYAPA_STATE_BL_BUSY & &
cyapa - > state < = CYAPA_STATE_BL_ACTIVE )
return true ;
return false ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 18:35:26 -08:00
static inline bool cyapa_is_operational_mode ( struct cyapa * cyapa )
2013-01-09 16:25:11 -08:00
{
2015-07-20 16:49:06 -07:00
if ( cyapa_is_pip_app_mode ( cyapa ) )
2015-01-17 18:35:26 -08:00
return true ;
if ( cyapa - > gen = = CYAPA_GEN3 & & cyapa - > state = = CYAPA_STATE_OP )
return true ;
return false ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 18:35:26 -08:00
/* Returns 0 on success, else negative errno on failure. */
static ssize_t cyapa_i2c_read ( struct cyapa * cyapa , u8 reg , size_t len ,
u8 * values )
2013-02-13 13:56:03 -08:00
{
struct i2c_client * client = cyapa - > client ;
2015-01-17 18:35:26 -08:00
struct i2c_msg msgs [ ] = {
{
. addr = client - > addr ,
. flags = 0 ,
. len = 1 ,
. buf = & reg ,
} ,
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = len ,
. buf = values ,
} ,
} ;
int ret ;
2013-02-13 13:56:03 -08:00
2015-01-17 18:35:26 -08:00
ret = i2c_transfer ( client - > adapter , msgs , ARRAY_SIZE ( msgs ) ) ;
2013-02-13 13:56:03 -08:00
2015-01-17 18:35:26 -08:00
if ( ret ! = ARRAY_SIZE ( msgs ) )
return ret < 0 ? ret : - EIO ;
2013-02-13 13:56:03 -08:00
2015-01-17 18:35:26 -08:00
return 0 ;
2013-02-13 13:56:03 -08:00
}
2015-01-17 18:35:26 -08:00
/**
* cyapa_i2c_write - Execute i2c block data write operation
* @ cyapa : Handle to this driver
* @ ret : Offset of the data to written in the register map
* @ len : number of bytes to write
* @ values : Data to be written
*
* Return negative errno code on error ; return zero when success .
*/
static int cyapa_i2c_write ( struct cyapa * cyapa , u8 reg ,
size_t len , const void * values )
2013-01-09 16:25:11 -08:00
{
2015-01-17 18:35:26 -08:00
struct i2c_client * client = cyapa - > client ;
char buf [ 32 ] ;
int ret ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
if ( len > sizeof ( buf ) - 1 )
return - ENOMEM ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
buf [ 0 ] = reg ;
memcpy ( & buf [ 1 ] , values , len ) ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
ret = i2c_master_send ( client , buf , len + 1 ) ;
if ( ret ! = len + 1 )
return ret < 0 ? ret : - EIO ;
return 0 ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 18:35:26 -08:00
static u8 cyapa_check_adapter_functionality ( struct i2c_client * client )
2013-01-09 16:25:11 -08:00
{
2015-01-17 18:35:26 -08:00
u8 ret = CYAPA_ADAPTER_FUNC_NONE ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
if ( i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) )
ret | = CYAPA_ADAPTER_FUNC_I2C ;
if ( i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK ) )
ret | = CYAPA_ADAPTER_FUNC_SMBUS ;
return ret ;
2013-01-09 16:25:11 -08:00
}
/*
* Query device for its current operating state .
*/
static int cyapa_get_state ( struct cyapa * cyapa )
{
u8 status [ BL_STATUS_SIZE ] ;
2015-01-17 18:35:26 -08:00
u8 cmd [ 32 ] ;
/* The i2c address of gen4 and gen5 trackpad device must be even. */
bool even_addr = ( ( cyapa - > client - > addr & 0x0001 ) = = 0 ) ;
bool smbus = false ;
int retries = 2 ;
2014-12-04 07:00:03 -08:00
int error ;
2013-01-09 16:25:11 -08:00
cyapa - > state = CYAPA_STATE_NO_DEVICE ;
/*
* Get trackpad status by reading 3 registers starting from 0.
* If the device is in the bootloader , this will be BL_HEAD .
* If the device is in operation mode , this will be the DATA regs .
*
*/
2014-12-04 07:00:03 -08:00
error = cyapa_i2c_reg_read_block ( cyapa , BL_HEAD_OFFSET , BL_STATUS_SIZE ,
2015-01-17 18:35:26 -08:00
status ) ;
2013-02-13 13:56:03 -08:00
/*
* On smbus systems in OP mode , the i2c_reg_read will fail with
* - ETIMEDOUT . In this case , try again using the smbus equivalent
* command . This should return a BL_HEAD indicating CYAPA_STATE_OP .
*/
2015-01-17 18:35:26 -08:00
if ( cyapa - > smbus & & ( error = = - ETIMEDOUT | | error = = - ENXIO ) ) {
if ( ! even_addr )
error = cyapa_read_block ( cyapa ,
CYAPA_CMD_BL_STATUS , status ) ;
smbus = true ;
}
2013-02-13 13:56:03 -08:00
2014-12-04 07:00:03 -08:00
if ( error ! = BL_STATUS_SIZE )
2013-01-09 16:25:11 -08:00
goto error ;
2015-01-17 18:35:26 -08:00
/*
* Detect trackpad protocol based on characteristic registers and bits .
*/
do {
cyapa - > status [ REG_OP_STATUS ] = status [ REG_OP_STATUS ] ;
cyapa - > status [ REG_BL_STATUS ] = status [ REG_BL_STATUS ] ;
cyapa - > status [ REG_BL_ERROR ] = status [ REG_BL_ERROR ] ;
if ( cyapa - > gen = = CYAPA_GEN_UNKNOWN | |
cyapa - > gen = = CYAPA_GEN3 ) {
error = cyapa_gen3_ops . state_parse ( cyapa ,
status , BL_STATUS_SIZE ) ;
if ( ! error )
goto out_detected ;
2013-01-09 16:25:11 -08:00
}
2015-07-20 16:53:30 -07:00
if ( cyapa - > gen = = CYAPA_GEN_UNKNOWN | |
cyapa - > gen = = CYAPA_GEN6 | |
cyapa - > gen = = CYAPA_GEN5 ) {
error = cyapa_pip_state_parse ( cyapa ,
status , BL_STATUS_SIZE ) ;
if ( ! error )
goto out_detected ;
}
/* For old Gen5 trackpads detecting. */
2015-01-17 18:49:37 -08:00
if ( ( cyapa - > gen = = CYAPA_GEN_UNKNOWN | |
cyapa - > gen = = CYAPA_GEN5 ) & &
! smbus & & even_addr ) {
error = cyapa_gen5_ops . state_parse ( cyapa ,
status , BL_STATUS_SIZE ) ;
if ( ! error )
goto out_detected ;
}
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
/*
* Write 0x00 0x00 to trackpad device to force update its
* status , then redo the detection again .
*/
if ( ! smbus ) {
cmd [ 0 ] = 0x00 ;
cmd [ 1 ] = 0x00 ;
error = cyapa_i2c_write ( cyapa , 0 , 2 , cmd ) ;
if ( error )
goto error ;
msleep ( 50 ) ;
error = cyapa_i2c_read ( cyapa , BL_HEAD_OFFSET ,
BL_STATUS_SIZE , status ) ;
if ( error )
goto error ;
}
} while ( - - retries > 0 & & ! smbus ) ;
goto error ;
out_detected :
if ( cyapa - > state < = CYAPA_STATE_BL_BUSY )
return - EAGAIN ;
2013-01-09 16:25:11 -08:00
return 0 ;
2015-01-17 18:35:26 -08:00
2013-01-09 16:25:11 -08:00
error :
2014-12-04 07:00:03 -08:00
return ( error < 0 ) ? error : - EAGAIN ;
2013-01-09 16:25:11 -08:00
}
/*
* Poll device for its status in a loop , waiting up to timeout for a response .
*
* When the device switches state , it usually takes ~ 300 ms .
* However , when running a new firmware image , the device must calibrate its
* sensors , which can take as long as 2 seconds .
*
* Note : The timeout has granularity of the polling rate , which is 100 ms .
*
* Returns :
* 0 when the device eventually responds with a valid non - busy state .
* - ETIMEDOUT if device never responds ( too many - EAGAIN )
2015-01-17 18:35:26 -08:00
* - EAGAIN if bootload is busy , or unknown state .
* < 0 other errors
2013-01-09 16:25:11 -08:00
*/
2015-01-17 18:35:26 -08:00
int cyapa_poll_state ( struct cyapa * cyapa , unsigned int timeout )
2013-01-09 16:25:11 -08:00
{
2014-12-04 07:00:03 -08:00
int error ;
2013-01-09 16:25:11 -08:00
int tries = timeout / 100 ;
2015-01-17 18:35:26 -08:00
do {
2014-12-04 07:00:03 -08:00
error = cyapa_get_state ( cyapa ) ;
2015-01-17 18:35:26 -08:00
if ( ! error & & cyapa - > state > CYAPA_STATE_BL_BUSY )
return 0 ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
msleep ( 100 ) ;
} while ( tries - - ) ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
return ( error = = - EAGAIN | | error = = - ETIMEDOUT ) ? - ETIMEDOUT : error ;
2013-01-09 16:25:11 -08:00
}
/*
* Check if device is operational .
*
* An operational device is responding , has exited bootloader , and has
* firmware supported by this driver .
*
* Returns :
2015-01-17 18:35:26 -08:00
* - ENODEV no device
2013-01-09 16:25:11 -08:00
* - EBUSY no device or in bootloader
* - EIO failure while reading from device
2015-01-17 18:35:26 -08:00
* - ETIMEDOUT timeout failure for bus idle or bus no response
2013-01-09 16:25:11 -08:00
* - EAGAIN device is still in bootloader
* if - > state = CYAPA_STATE_BL_IDLE , device has invalid firmware
* - EINVAL device is in operational mode , but not supported by this driver
* 0 device is supported
*/
static int cyapa_check_is_operational ( struct cyapa * cyapa )
{
2014-12-04 07:00:03 -08:00
int error ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
error = cyapa_poll_state ( cyapa , 4000 ) ;
2014-12-04 07:00:03 -08:00
if ( error )
return error ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
switch ( cyapa - > gen ) {
2015-07-20 16:53:30 -07:00
case CYAPA_GEN6 :
cyapa - > ops = & cyapa_gen6_ops ;
break ;
2015-01-17 18:49:37 -08:00
case CYAPA_GEN5 :
cyapa - > ops = & cyapa_gen5_ops ;
break ;
2015-01-17 18:35:26 -08:00
case CYAPA_GEN3 :
cyapa - > ops = & cyapa_gen3_ops ;
break ;
2013-01-09 16:25:11 -08:00
default :
2015-01-17 18:35:26 -08:00
return - ENODEV ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 18:35:26 -08:00
error = cyapa - > ops - > operational_check ( cyapa ) ;
if ( ! error & & cyapa_is_operational_mode ( cyapa ) )
cyapa - > operational = true ;
else
cyapa - > operational = false ;
return error ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 18:35:26 -08:00
/*
* Returns 0 on device detected , negative errno on no device detected .
2015-07-20 16:49:06 -07:00
* And when the device is detected and operational , it will be reset to
2015-01-17 18:35:26 -08:00
* full power active mode automatically .
*/
static int cyapa_detect ( struct cyapa * cyapa )
2013-01-09 16:25:11 -08:00
{
struct device * dev = & cyapa - > client - > dev ;
2015-01-17 18:35:26 -08:00
int error ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
error = cyapa_check_is_operational ( cyapa ) ;
if ( error ) {
if ( error ! = - ETIMEDOUT & & error ! = - ENODEV & &
cyapa_is_bootloader_mode ( cyapa ) ) {
dev_warn ( dev , " device detected but not operational \n " ) ;
return 0 ;
}
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
dev_err ( dev , " no device detected: %d \n " , error ) ;
return error ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 18:35:26 -08:00
return 0 ;
2013-02-13 13:56:03 -08:00
}
2014-11-09 12:36:34 -08:00
static int cyapa_open ( struct input_dev * input )
{
struct cyapa * cyapa = input_get_drvdata ( input ) ;
struct i2c_client * client = cyapa - > client ;
2015-07-20 17:09:59 -07:00
struct device * dev = & client - > dev ;
2014-11-09 12:36:34 -08:00
int error ;
2015-01-17 18:35:26 -08:00
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
2014-11-09 12:36:34 -08:00
return error ;
2015-01-17 18:35:26 -08:00
if ( cyapa - > operational ) {
/*
* though failed to set active power mode ,
* but still may be able to work in lower scan rate
* when in operational mode .
*/
error = cyapa - > ops - > set_power_mode ( cyapa ,
2016-03-04 11:23:09 -08:00
PWR_MODE_FULL_ACTIVE , 0 , CYAPA_PM_ACTIVE ) ;
2015-01-17 18:35:26 -08:00
if ( error ) {
2015-07-20 17:09:59 -07:00
dev_warn ( dev , " set active power failed: %d \n " , error ) ;
2015-01-17 18:35:26 -08:00
goto out ;
}
} else {
error = cyapa_reinitialize ( cyapa ) ;
if ( error | | ! cyapa - > operational ) {
error = error ? error : - EAGAIN ;
goto out ;
}
2014-11-09 12:36:34 -08:00
}
enable_irq ( client - > irq ) ;
2015-07-20 17:09:59 -07:00
if ( ! pm_runtime_enabled ( dev ) ) {
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
2015-01-17 18:57:42 -08:00
}
2015-07-20 17:09:59 -07:00
pm_runtime_get_sync ( dev ) ;
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_put_sync_autosuspend ( dev ) ;
2015-01-17 18:35:26 -08:00
out :
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return error ;
2014-11-09 12:36:34 -08:00
}
static void cyapa_close ( struct input_dev * input )
{
struct cyapa * cyapa = input_get_drvdata ( input ) ;
2015-01-17 18:35:26 -08:00
struct i2c_client * client = cyapa - > client ;
2015-07-20 17:09:59 -07:00
struct device * dev = & cyapa - > client - > dev ;
2015-01-17 18:35:26 -08:00
mutex_lock ( & cyapa - > state_sync_lock ) ;
2014-11-09 12:36:34 -08:00
2015-01-17 18:35:26 -08:00
disable_irq ( client - > irq ) ;
2015-07-20 17:09:59 -07:00
if ( pm_runtime_enabled ( dev ) )
pm_runtime_disable ( dev ) ;
pm_runtime_set_suspended ( dev ) ;
2015-01-17 18:57:42 -08:00
2015-01-17 18:35:26 -08:00
if ( cyapa - > operational )
2016-03-04 11:23:09 -08:00
cyapa - > ops - > set_power_mode ( cyapa ,
PWR_MODE_OFF , 0 , CYAPA_PM_DEACTIVE ) ;
2015-01-17 18:35:26 -08:00
mutex_unlock ( & cyapa - > state_sync_lock ) ;
2014-11-09 12:36:34 -08:00
}
2013-01-09 16:25:11 -08:00
static int cyapa_create_input_dev ( struct cyapa * cyapa )
{
struct device * dev = & cyapa - > client - > dev ;
struct input_dev * input ;
2014-11-09 12:36:34 -08:00
int error ;
2013-01-09 16:25:11 -08:00
if ( ! cyapa - > physical_size_x | | ! cyapa - > physical_size_y )
return - EINVAL ;
2014-11-09 12:36:34 -08:00
input = devm_input_allocate_device ( dev ) ;
2013-01-09 16:25:11 -08:00
if ( ! input ) {
2014-12-04 07:00:03 -08:00
dev_err ( dev , " failed to allocate memory for input device. \n " ) ;
2013-01-09 16:25:11 -08:00
return - ENOMEM ;
}
input - > name = CYAPA_NAME ;
input - > phys = cyapa - > phys ;
input - > id . bustype = BUS_I2C ;
input - > id . version = 1 ;
2014-12-04 07:00:03 -08:00
input - > id . product = 0 ; /* Means any product in eventcomm. */
2013-01-09 16:25:11 -08:00
input - > dev . parent = & cyapa - > client - > dev ;
2014-11-09 12:36:34 -08:00
input - > open = cyapa_open ;
input - > close = cyapa_close ;
2013-01-09 16:25:11 -08:00
input_set_drvdata ( input , cyapa ) ;
__set_bit ( EV_ABS , input - > evbit ) ;
2014-12-04 07:00:03 -08:00
/* Finger position */
2013-01-09 16:25:11 -08:00
input_set_abs_params ( input , ABS_MT_POSITION_X , 0 , cyapa - > max_abs_x , 0 ,
0 ) ;
input_set_abs_params ( input , ABS_MT_POSITION_Y , 0 , cyapa - > max_abs_y , 0 ,
0 ) ;
2015-01-17 18:35:26 -08:00
input_set_abs_params ( input , ABS_MT_PRESSURE , 0 , cyapa - > max_z , 0 , 0 ) ;
if ( cyapa - > gen > CYAPA_GEN3 ) {
input_set_abs_params ( input , ABS_MT_TOUCH_MAJOR , 0 , 255 , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_TOUCH_MINOR , 0 , 255 , 0 , 0 ) ;
/*
* Orientation is the angle between the vertical axis and
* the major axis of the contact ellipse .
* The range is - 127 to 127.
* the positive direction is clockwise form the vertical axis .
* If the ellipse of contact degenerates into a circle ,
* orientation is reported as 0.
*
* Also , for Gen5 trackpad the accurate of this orientation
* value is value + ( - 30 ~ 30 ) .
*/
input_set_abs_params ( input , ABS_MT_ORIENTATION ,
- 127 , 127 , 0 , 0 ) ;
}
if ( cyapa - > gen > = CYAPA_GEN5 ) {
input_set_abs_params ( input , ABS_MT_WIDTH_MAJOR , 0 , 255 , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_WIDTH_MINOR , 0 , 255 , 0 , 0 ) ;
2015-07-20 16:57:53 -07:00
input_set_abs_params ( input , ABS_DISTANCE , 0 , 1 , 0 , 0 ) ;
2015-01-17 18:35:26 -08:00
}
2013-01-09 16:25:11 -08:00
input_abs_set_res ( input , ABS_MT_POSITION_X ,
cyapa - > max_abs_x / cyapa - > physical_size_x ) ;
input_abs_set_res ( input , ABS_MT_POSITION_Y ,
cyapa - > max_abs_y / cyapa - > physical_size_y ) ;
if ( cyapa - > btn_capability & CAPABILITY_LEFT_BTN_MASK )
__set_bit ( BTN_LEFT , input - > keybit ) ;
if ( cyapa - > btn_capability & CAPABILITY_MIDDLE_BTN_MASK )
__set_bit ( BTN_MIDDLE , input - > keybit ) ;
if ( cyapa - > btn_capability & CAPABILITY_RIGHT_BTN_MASK )
__set_bit ( BTN_RIGHT , input - > keybit ) ;
if ( cyapa - > btn_capability = = CAPABILITY_LEFT_BTN_MASK )
__set_bit ( INPUT_PROP_BUTTONPAD , input - > propbit ) ;
2014-12-04 07:00:03 -08:00
/* Handle pointer emulation and unused slots in core */
2014-11-09 12:36:34 -08:00
error = input_mt_init_slots ( input , CYAPA_MAX_MT_SLOTS ,
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED ) ;
if ( error ) {
dev_err ( dev , " failed to initialize MT slots: %d \n " , error ) ;
return error ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 18:35:26 -08:00
/* Register the device in input subsystem */
error = input_register_device ( input ) ;
if ( error ) {
dev_err ( dev , " failed to register input device: %d \n " , error ) ;
return error ;
}
2014-11-09 12:36:34 -08:00
cyapa - > input = input ;
2013-01-09 16:25:11 -08:00
return 0 ;
}
2015-01-17 22:07:12 -08:00
static void cyapa_enable_irq_for_cmd ( struct cyapa * cyapa )
{
struct input_dev * input = cyapa - > input ;
if ( ! input | | ! input - > users ) {
/*
* When input is NULL , TP must be in deep sleep mode .
* In this mode , later non - power I2C command will always failed
* if not bring it out of deep sleep mode firstly ,
* so must command TP to active mode here .
*/
if ( ! input | | cyapa - > operational )
cyapa - > ops - > set_power_mode ( cyapa ,
2016-03-04 11:23:09 -08:00
PWR_MODE_FULL_ACTIVE , 0 , CYAPA_PM_ACTIVE ) ;
2015-01-17 22:07:12 -08:00
/* Gen3 always using polling mode for command. */
if ( cyapa - > gen > = CYAPA_GEN5 )
enable_irq ( cyapa - > client - > irq ) ;
}
}
static void cyapa_disable_irq_for_cmd ( struct cyapa * cyapa )
{
struct input_dev * input = cyapa - > input ;
if ( ! input | | ! input - > users ) {
if ( cyapa - > gen > = CYAPA_GEN5 )
disable_irq ( cyapa - > client - > irq ) ;
if ( ! input | | cyapa - > operational )
2015-07-20 17:09:59 -07:00
cyapa - > ops - > set_power_mode ( cyapa ,
2016-03-04 11:23:09 -08:00
PWR_MODE_OFF , 0 , CYAPA_PM_ACTIVE ) ;
2015-01-17 22:07:12 -08:00
}
}
2015-01-17 18:35:26 -08:00
/*
* cyapa_sleep_time_to_pwr_cmd and cyapa_pwr_cmd_to_sleep_time
*
* These are helper functions that convert to and from integer idle
* times and register settings to write to the PowerMode register .
* The trackpad supports between 20 ms to 1000 ms scan intervals .
* The time will be increased in increments of 10 ms from 20 ms to 100 ms .
* From 100 ms to 1000 ms , time will be increased in increments of 20 ms .
*
* When Idle_Time < 100 , the format to convert Idle_Time to Idle_Command is :
* Idle_Command = Idle Time / 10 ;
* When Idle_Time > = 100 , the format to convert Idle_Time to Idle_Command is :
* Idle_Command = Idle Time / 20 + 5 ;
*/
u8 cyapa_sleep_time_to_pwr_cmd ( u16 sleep_time )
{
u16 encoded_time ;
sleep_time = clamp_val ( sleep_time , 20 , 1000 ) ;
encoded_time = sleep_time < 100 ? sleep_time / 10 : sleep_time / 20 + 5 ;
return ( encoded_time < < 2 ) & PWR_MODE_MASK ;
}
u16 cyapa_pwr_cmd_to_sleep_time ( u8 pwr_mode )
{
u8 encoded_time = pwr_mode > > 2 ;
return ( encoded_time < 10 ) ? encoded_time * 10
: ( encoded_time - 5 ) * 20 ;
}
/* 0 on driver initialize and detected successfully, negative on failure. */
static int cyapa_initialize ( struct cyapa * cyapa )
{
int error = 0 ;
cyapa - > state = CYAPA_STATE_NO_DEVICE ;
cyapa - > gen = CYAPA_GEN_UNKNOWN ;
mutex_init ( & cyapa - > state_sync_lock ) ;
/*
* Set to hard code default , they will be updated with trackpad set
* default values after probe and initialized .
*/
cyapa - > suspend_power_mode = PWR_MODE_SLEEP ;
cyapa - > suspend_sleep_time =
cyapa_pwr_cmd_to_sleep_time ( cyapa - > suspend_power_mode ) ;
/* ops.initialize() is aimed to prepare for module communications. */
error = cyapa_gen3_ops . initialize ( cyapa ) ;
2015-01-17 18:49:37 -08:00
if ( ! error )
error = cyapa_gen5_ops . initialize ( cyapa ) ;
2015-07-20 16:53:30 -07:00
if ( ! error )
error = cyapa_gen6_ops . initialize ( cyapa ) ;
2015-01-17 18:35:26 -08:00
if ( error )
return error ;
error = cyapa_detect ( cyapa ) ;
if ( error )
return error ;
/* Power down the device until we need it. */
if ( cyapa - > operational )
2016-03-04 11:23:09 -08:00
cyapa - > ops - > set_power_mode ( cyapa ,
PWR_MODE_OFF , 0 , CYAPA_PM_ACTIVE ) ;
2015-01-17 18:35:26 -08:00
return 0 ;
}
static int cyapa_reinitialize ( struct cyapa * cyapa )
{
struct device * dev = & cyapa - > client - > dev ;
struct input_dev * input = cyapa - > input ;
int error ;
2015-01-17 18:57:42 -08:00
if ( pm_runtime_enabled ( dev ) )
pm_runtime_disable ( dev ) ;
2015-01-17 18:35:26 -08:00
/* Avoid command failures when TP was in OFF state. */
if ( cyapa - > operational )
2015-07-20 17:09:59 -07:00
cyapa - > ops - > set_power_mode ( cyapa ,
2016-03-04 11:23:09 -08:00
PWR_MODE_FULL_ACTIVE , 0 , CYAPA_PM_ACTIVE ) ;
2015-01-17 18:35:26 -08:00
error = cyapa_detect ( cyapa ) ;
if ( error )
goto out ;
if ( ! input & & cyapa - > operational ) {
error = cyapa_create_input_dev ( cyapa ) ;
if ( error ) {
dev_err ( dev , " create input_dev instance failed: %d \n " ,
error ) ;
goto out ;
}
}
out :
if ( ! input | | ! input - > users ) {
/* Reset to power OFF state to save power when no user open. */
if ( cyapa - > operational )
2015-07-20 17:09:59 -07:00
cyapa - > ops - > set_power_mode ( cyapa ,
2016-03-04 11:23:09 -08:00
PWR_MODE_OFF , 0 , CYAPA_PM_DEACTIVE ) ;
2015-01-17 18:57:42 -08:00
} else if ( ! error & & cyapa - > operational ) {
/*
* Make sure only enable runtime PM when device is
* in operational mode and input - > users > 0.
*/
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
2015-07-20 17:09:59 -07:00
pm_runtime_get_sync ( dev ) ;
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_put_sync_autosuspend ( dev ) ;
2015-01-17 18:35:26 -08:00
}
return error ;
}
static irqreturn_t cyapa_irq ( int irq , void * dev_id )
{
struct cyapa * cyapa = dev_id ;
struct device * dev = & cyapa - > client - > dev ;
2015-07-20 17:09:59 -07:00
int error ;
2015-01-17 18:35:26 -08:00
if ( device_may_wakeup ( dev ) )
pm_wakeup_event ( dev , 0 ) ;
2015-07-20 16:49:06 -07:00
/* Interrupt event can be caused by host command to trackpad device. */
2015-01-17 18:35:26 -08:00
if ( cyapa - > ops - > irq_cmd_handler ( cyapa ) ) {
/*
* Interrupt event maybe from trackpad device input reporting .
*/
if ( ! cyapa - > input ) {
/*
2015-07-20 16:49:06 -07:00
* Still in probing or in firmware image
* updating or reading .
2015-01-17 18:35:26 -08:00
*/
cyapa - > ops - > sort_empty_output_data ( cyapa ,
NULL , NULL , NULL ) ;
goto out ;
}
2015-07-20 17:09:59 -07:00
if ( cyapa - > operational ) {
error = cyapa - > ops - > irq_handler ( cyapa ) ;
/*
* Apply runtime power management to touch report event
* except the events caused by the command responses .
* Note :
* It will introduce about 20 ~ 40 ms additional delay
* time in receiving for first valid touch report data .
* The time is used to execute device runtime resume
* process .
*/
pm_runtime_get_sync ( dev ) ;
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_put_sync_autosuspend ( dev ) ;
}
if ( ! cyapa - > operational | | error ) {
2015-01-17 18:35:26 -08:00
if ( ! mutex_trylock ( & cyapa - > state_sync_lock ) ) {
cyapa - > ops - > sort_empty_output_data ( cyapa ,
NULL , NULL , NULL ) ;
goto out ;
}
cyapa_reinitialize ( cyapa ) ;
mutex_unlock ( & cyapa - > state_sync_lock ) ;
}
}
out :
return IRQ_HANDLED ;
}
2015-01-17 18:56:18 -08:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* sysfs interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
# ifdef CONFIG_PM_SLEEP
static ssize_t cyapa_show_suspend_scanrate ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
u8 pwr_cmd = cyapa - > suspend_power_mode ;
u16 sleep_time ;
int len ;
int error ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
pwr_cmd = cyapa - > suspend_power_mode ;
sleep_time = cyapa - > suspend_sleep_time ;
mutex_unlock ( & cyapa - > state_sync_lock ) ;
switch ( pwr_cmd ) {
case PWR_MODE_BTN_ONLY :
len = scnprintf ( buf , PAGE_SIZE , " %s \n " , BTN_ONLY_MODE_NAME ) ;
break ;
case PWR_MODE_OFF :
len = scnprintf ( buf , PAGE_SIZE , " %s \n " , OFF_MODE_NAME ) ;
break ;
default :
len = scnprintf ( buf , PAGE_SIZE , " %u \n " ,
cyapa - > gen = = CYAPA_GEN3 ?
cyapa_pwr_cmd_to_sleep_time ( pwr_cmd ) :
sleep_time ) ;
break ;
}
return len ;
}
static ssize_t cyapa_update_suspend_scanrate ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
u16 sleep_time ;
int error ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
if ( sysfs_streq ( buf , BTN_ONLY_MODE_NAME ) ) {
cyapa - > suspend_power_mode = PWR_MODE_BTN_ONLY ;
} else if ( sysfs_streq ( buf , OFF_MODE_NAME ) ) {
cyapa - > suspend_power_mode = PWR_MODE_OFF ;
} else if ( ! kstrtou16 ( buf , 10 , & sleep_time ) ) {
2015-04-20 10:00:05 -07:00
cyapa - > suspend_sleep_time = min_t ( u16 , sleep_time , 1000 ) ;
2015-01-17 18:56:18 -08:00
cyapa - > suspend_power_mode =
cyapa_sleep_time_to_pwr_cmd ( cyapa - > suspend_sleep_time ) ;
} else {
count = - EINVAL ;
}
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return count ;
}
static DEVICE_ATTR ( suspend_scanrate_ms , S_IRUGO | S_IWUSR ,
cyapa_show_suspend_scanrate ,
cyapa_update_suspend_scanrate ) ;
static struct attribute * cyapa_power_wakeup_entries [ ] = {
& dev_attr_suspend_scanrate_ms . attr ,
NULL ,
} ;
static const struct attribute_group cyapa_power_wakeup_group = {
. name = power_group_name ,
. attrs = cyapa_power_wakeup_entries ,
} ;
static void cyapa_remove_power_wakeup_group ( void * data )
{
struct cyapa * cyapa = data ;
sysfs_unmerge_group ( & cyapa - > client - > dev . kobj ,
& cyapa_power_wakeup_group ) ;
}
static int cyapa_prepare_wakeup_controls ( struct cyapa * cyapa )
{
struct i2c_client * client = cyapa - > client ;
struct device * dev = & client - > dev ;
int error ;
if ( device_can_wakeup ( dev ) ) {
error = sysfs_merge_group ( & client - > dev . kobj ,
& cyapa_power_wakeup_group ) ;
if ( error ) {
dev_err ( dev , " failed to add power wakeup group: %d \n " ,
error ) ;
return error ;
}
error = devm_add_action ( dev ,
cyapa_remove_power_wakeup_group , cyapa ) ;
if ( error ) {
cyapa_remove_power_wakeup_group ( cyapa ) ;
dev_err ( dev , " failed to add power cleanup action: %d \n " ,
error ) ;
return error ;
}
}
return 0 ;
}
# else
static inline int cyapa_prepare_wakeup_controls ( struct cyapa * cyapa )
{
return 0 ;
}
# endif /* CONFIG_PM_SLEEP */
2015-01-17 18:57:42 -08:00
# ifdef CONFIG_PM
static ssize_t cyapa_show_rt_suspend_scanrate ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
u8 pwr_cmd ;
u16 sleep_time ;
int error ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
pwr_cmd = cyapa - > runtime_suspend_power_mode ;
sleep_time = cyapa - > runtime_suspend_sleep_time ;
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return scnprintf ( buf , PAGE_SIZE , " %u \n " ,
cyapa - > gen = = CYAPA_GEN3 ?
cyapa_pwr_cmd_to_sleep_time ( pwr_cmd ) :
sleep_time ) ;
}
static ssize_t cyapa_update_rt_suspend_scanrate ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
u16 time ;
int error ;
if ( buf = = NULL | | count = = 0 | | kstrtou16 ( buf , 10 , & time ) ) {
dev_err ( dev , " invalid runtime suspend scanrate ms parameter \n " ) ;
return - EINVAL ;
}
/*
* When the suspend scanrate is changed , pm_runtime_get to resume
* a potentially suspended device , update to the new pwr_cmd
* and then pm_runtime_put to suspend into the new power mode .
*/
pm_runtime_get_sync ( dev ) ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
2015-04-20 10:00:05 -07:00
cyapa - > runtime_suspend_sleep_time = min_t ( u16 , time , 1000 ) ;
2015-01-17 18:57:42 -08:00
cyapa - > runtime_suspend_power_mode =
cyapa_sleep_time_to_pwr_cmd ( cyapa - > runtime_suspend_sleep_time ) ;
mutex_unlock ( & cyapa - > state_sync_lock ) ;
pm_runtime_put_sync_autosuspend ( dev ) ;
return count ;
}
static DEVICE_ATTR ( runtime_suspend_scanrate_ms , S_IRUGO | S_IWUSR ,
cyapa_show_rt_suspend_scanrate ,
cyapa_update_rt_suspend_scanrate ) ;
static struct attribute * cyapa_power_runtime_entries [ ] = {
& dev_attr_runtime_suspend_scanrate_ms . attr ,
NULL ,
} ;
static const struct attribute_group cyapa_power_runtime_group = {
. name = power_group_name ,
. attrs = cyapa_power_runtime_entries ,
} ;
static void cyapa_remove_power_runtime_group ( void * data )
{
struct cyapa * cyapa = data ;
sysfs_unmerge_group ( & cyapa - > client - > dev . kobj ,
& cyapa_power_runtime_group ) ;
}
static int cyapa_start_runtime ( struct cyapa * cyapa )
{
struct device * dev = & cyapa - > client - > dev ;
int error ;
cyapa - > runtime_suspend_power_mode = PWR_MODE_IDLE ;
cyapa - > runtime_suspend_sleep_time =
cyapa_pwr_cmd_to_sleep_time ( cyapa - > runtime_suspend_power_mode ) ;
error = sysfs_merge_group ( & dev - > kobj , & cyapa_power_runtime_group ) ;
if ( error ) {
dev_err ( dev ,
" failed to create power runtime group: %d \n " , error ) ;
return error ;
}
error = devm_add_action ( dev , cyapa_remove_power_runtime_group , cyapa ) ;
if ( error ) {
cyapa_remove_power_runtime_group ( cyapa ) ;
dev_err ( dev ,
" failed to add power runtime cleanup action: %d \n " ,
error ) ;
return error ;
}
/* runtime is enabled until device is operational and opened. */
pm_runtime_set_suspended ( dev ) ;
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_set_autosuspend_delay ( dev , AUTOSUSPEND_DELAY ) ;
return 0 ;
}
# else
static inline int cyapa_start_runtime ( struct cyapa * cyapa )
{
return 0 ;
}
# endif /* CONFIG_PM */
2015-01-17 22:07:12 -08:00
static ssize_t cyapa_show_fm_ver ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int error ;
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
error = scnprintf ( buf , PAGE_SIZE , " %d.%d \n " , cyapa - > fw_maj_ver ,
cyapa - > fw_min_ver ) ;
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return error ;
}
static ssize_t cyapa_show_product_id ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
int size ;
int error ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
size = scnprintf ( buf , PAGE_SIZE , " %s \n " , cyapa - > product_id ) ;
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return size ;
}
static int cyapa_firmware ( struct cyapa * cyapa , const char * fw_name )
{
struct device * dev = & cyapa - > client - > dev ;
const struct firmware * fw ;
int error ;
error = request_firmware ( & fw , fw_name , dev ) ;
if ( error ) {
dev_err ( dev , " Could not load firmware from %s: %d \n " ,
fw_name , error ) ;
return error ;
}
error = cyapa - > ops - > check_fw ( cyapa , fw ) ;
if ( error ) {
dev_err ( dev , " Invalid CYAPA firmware image: %s \n " ,
fw_name ) ;
goto done ;
}
/*
* Resume the potentially suspended device because doing FW
* update on a device not in the FULL mode has a chance to
* fail .
*/
pm_runtime_get_sync ( dev ) ;
/* Require IRQ support for firmware update commands. */
cyapa_enable_irq_for_cmd ( cyapa ) ;
error = cyapa - > ops - > bl_enter ( cyapa ) ;
if ( error ) {
dev_err ( dev , " bl_enter failed, %d \n " , error ) ;
goto err_detect ;
}
error = cyapa - > ops - > bl_activate ( cyapa ) ;
if ( error ) {
dev_err ( dev , " bl_activate failed, %d \n " , error ) ;
goto err_detect ;
}
error = cyapa - > ops - > bl_initiate ( cyapa , fw ) ;
if ( error ) {
dev_err ( dev , " bl_initiate failed, %d \n " , error ) ;
goto err_detect ;
}
error = cyapa - > ops - > update_fw ( cyapa , fw ) ;
if ( error ) {
dev_err ( dev , " update_fw failed, %d \n " , error ) ;
goto err_detect ;
}
err_detect :
cyapa_disable_irq_for_cmd ( cyapa ) ;
pm_runtime_put_noidle ( dev ) ;
done :
release_firmware ( fw ) ;
return error ;
}
static ssize_t cyapa_update_fw_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
char fw_name [ NAME_MAX ] ;
int ret , error ;
2015-01-22 08:20:16 -08:00
if ( count > = NAME_MAX ) {
2015-01-17 22:07:12 -08:00
dev_err ( dev , " File name too long \n " ) ;
return - EINVAL ;
}
memcpy ( fw_name , buf , count ) ;
if ( fw_name [ count - 1 ] = = ' \n ' )
fw_name [ count - 1 ] = ' \0 ' ;
else
fw_name [ count ] = ' \0 ' ;
if ( cyapa - > input ) {
/*
* Force the input device to be registered after the firmware
* image is updated , so if the corresponding parameters updated
* in the new firmware image can taken effect immediately .
*/
input_unregister_device ( cyapa - > input ) ;
cyapa - > input = NULL ;
}
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error ) {
/*
* Whatever , do reinitialize to try to recover TP state to
* previous state just as it entered fw update entrance .
*/
cyapa_reinitialize ( cyapa ) ;
return error ;
}
error = cyapa_firmware ( cyapa , fw_name ) ;
if ( error )
dev_err ( dev , " firmware update failed: %d \n " , error ) ;
else
dev_dbg ( dev , " firmware update successfully done. \n " ) ;
/*
2015-07-20 16:49:06 -07:00
* Re - detect trackpad device states because firmware update process
2015-01-17 22:07:12 -08:00
* will reset trackpad device into bootloader mode .
*/
ret = cyapa_reinitialize ( cyapa ) ;
if ( ret ) {
2015-07-20 16:49:06 -07:00
dev_err ( dev , " failed to re-detect after updated: %d \n " , ret ) ;
2015-01-17 22:07:12 -08:00
error = error ? error : ret ;
}
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return error ? error : count ;
}
static ssize_t cyapa_calibrate_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
int error ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
if ( cyapa - > operational ) {
cyapa_enable_irq_for_cmd ( cyapa ) ;
error = cyapa - > ops - > calibrate_store ( dev , attr , buf , count ) ;
cyapa_disable_irq_for_cmd ( cyapa ) ;
} else {
error = - EBUSY ; /* Still running in bootloader mode. */
}
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return error < 0 ? error : count ;
}
static ssize_t cyapa_show_baseline ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
ssize_t error ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
if ( cyapa - > operational ) {
cyapa_enable_irq_for_cmd ( cyapa ) ;
error = cyapa - > ops - > show_baseline ( dev , attr , buf ) ;
cyapa_disable_irq_for_cmd ( cyapa ) ;
} else {
error = - EBUSY ; /* Still running in bootloader mode. */
}
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return error ;
}
static char * cyapa_state_to_string ( struct cyapa * cyapa )
{
switch ( cyapa - > state ) {
case CYAPA_STATE_BL_BUSY :
return " bootloader busy " ;
case CYAPA_STATE_BL_IDLE :
return " bootloader idle " ;
case CYAPA_STATE_BL_ACTIVE :
return " bootloader active " ;
case CYAPA_STATE_GEN5_BL :
2015-07-20 16:53:30 -07:00
case CYAPA_STATE_GEN6_BL :
2015-01-17 22:07:12 -08:00
return " bootloader " ;
case CYAPA_STATE_OP :
case CYAPA_STATE_GEN5_APP :
2015-07-20 16:53:30 -07:00
case CYAPA_STATE_GEN6_APP :
2015-01-17 22:07:12 -08:00
return " operational " ; /* Normal valid state. */
default :
return " invalid mode " ;
}
}
static ssize_t cyapa_show_mode ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
int size ;
int error ;
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
if ( error )
return error ;
size = scnprintf ( buf , PAGE_SIZE , " gen%d %s \n " ,
cyapa - > gen , cyapa_state_to_string ( cyapa ) ) ;
mutex_unlock ( & cyapa - > state_sync_lock ) ;
return size ;
}
static DEVICE_ATTR ( firmware_version , S_IRUGO , cyapa_show_fm_ver , NULL ) ;
static DEVICE_ATTR ( product_id , S_IRUGO , cyapa_show_product_id , NULL ) ;
static DEVICE_ATTR ( update_fw , S_IWUSR , NULL , cyapa_update_fw_store ) ;
static DEVICE_ATTR ( baseline , S_IRUGO , cyapa_show_baseline , NULL ) ;
static DEVICE_ATTR ( calibrate , S_IWUSR , NULL , cyapa_calibrate_store ) ;
static DEVICE_ATTR ( mode , S_IRUGO , cyapa_show_mode , NULL ) ;
static struct attribute * cyapa_sysfs_entries [ ] = {
& dev_attr_firmware_version . attr ,
& dev_attr_product_id . attr ,
& dev_attr_update_fw . attr ,
& dev_attr_baseline . attr ,
& dev_attr_calibrate . attr ,
& dev_attr_mode . attr ,
NULL ,
} ;
static const struct attribute_group cyapa_sysfs_group = {
. attrs = cyapa_sysfs_entries ,
} ;
static void cyapa_remove_sysfs_group ( void * data )
{
struct cyapa * cyapa = data ;
sysfs_remove_group ( & cyapa - > client - > dev . kobj , & cyapa_sysfs_group ) ;
}
2015-07-30 11:19:18 -07:00
static void cyapa_disable_regulator ( void * data )
{
struct cyapa * cyapa = data ;
regulator_disable ( cyapa - > vcc ) ;
}
2013-01-09 16:25:11 -08:00
static int cyapa_probe ( struct i2c_client * client ,
const struct i2c_device_id * dev_id )
{
struct device * dev = & client - > dev ;
2014-11-09 12:36:34 -08:00
struct cyapa * cyapa ;
u8 adapter_func ;
2015-01-17 18:35:26 -08:00
union i2c_smbus_data dummy ;
2014-11-09 12:36:34 -08:00
int error ;
2013-01-09 16:25:11 -08:00
2013-02-13 13:56:03 -08:00
adapter_func = cyapa_check_adapter_functionality ( client ) ;
if ( adapter_func = = CYAPA_ADAPTER_FUNC_NONE ) {
dev_err ( dev , " not a supported I2C/SMBus adapter \n " ) ;
return - EIO ;
}
2015-01-17 18:35:26 -08:00
/* Make sure there is something at this address */
if ( i2c_smbus_xfer ( client - > adapter , client - > addr , 0 ,
I2C_SMBUS_READ , 0 , I2C_SMBUS_BYTE , & dummy ) < 0 )
return - ENODEV ;
2014-11-09 12:36:34 -08:00
cyapa = devm_kzalloc ( dev , sizeof ( struct cyapa ) , GFP_KERNEL ) ;
if ( ! cyapa )
2013-01-09 16:25:11 -08:00
return - ENOMEM ;
2013-02-13 13:56:03 -08:00
/* i2c isn't supported, use smbus */
if ( adapter_func = = CYAPA_ADAPTER_FUNC_SMBUS )
cyapa - > smbus = true ;
2014-11-09 12:36:34 -08:00
2015-01-17 18:35:26 -08:00
cyapa - > client = client ;
i2c_set_clientdata ( client , cyapa ) ;
sprintf ( cyapa - > phys , " i2c-%d-%04x/input0 " , client - > adapter - > nr ,
client - > addr ) ;
2013-01-09 16:25:11 -08:00
2015-07-30 11:19:18 -07:00
cyapa - > vcc = devm_regulator_get ( dev , " vcc " ) ;
if ( IS_ERR ( cyapa - > vcc ) ) {
error = PTR_ERR ( cyapa - > vcc ) ;
dev_err ( dev , " failed to get vcc regulator: %d \n " , error ) ;
return error ;
}
error = regulator_enable ( cyapa - > vcc ) ;
if ( error ) {
dev_err ( dev , " failed to enable regulator: %d \n " , error ) ;
return error ;
}
error = devm_add_action ( dev , cyapa_disable_regulator , cyapa ) ;
if ( error ) {
cyapa_disable_regulator ( cyapa ) ;
dev_err ( dev , " failed to add disable regulator action: %d \n " ,
error ) ;
return error ;
}
2015-01-17 18:35:26 -08:00
error = cyapa_initialize ( cyapa ) ;
2014-11-09 12:36:34 -08:00
if ( error ) {
2015-01-17 18:35:26 -08:00
dev_err ( dev , " failed to detect and initialize tp device. \n " ) ;
2014-11-09 12:36:34 -08:00
return error ;
2013-01-09 16:25:11 -08:00
}
2015-01-17 22:07:12 -08:00
error = sysfs_create_group ( & client - > dev . kobj , & cyapa_sysfs_group ) ;
if ( error ) {
dev_err ( dev , " failed to create sysfs entries: %d \n " , error ) ;
return error ;
}
error = devm_add_action ( dev , cyapa_remove_sysfs_group , cyapa ) ;
if ( error ) {
cyapa_remove_sysfs_group ( cyapa ) ;
dev_err ( dev , " failed to add sysfs cleanup action: %d \n " , error ) ;
return error ;
}
2015-01-17 18:56:18 -08:00
error = cyapa_prepare_wakeup_controls ( cyapa ) ;
if ( error ) {
dev_err ( dev , " failed to prepare wakeup controls: %d \n " , error ) ;
return error ;
}
2015-01-17 18:57:42 -08:00
error = cyapa_start_runtime ( cyapa ) ;
if ( error ) {
dev_err ( dev , " failed to start pm_runtime: %d \n " , error ) ;
return error ;
}
2014-11-09 12:36:34 -08:00
error = devm_request_threaded_irq ( dev , client - > irq ,
NULL , cyapa_irq ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
" cyapa " , cyapa ) ;
if ( error ) {
2014-12-04 07:00:03 -08:00
dev_err ( dev , " failed to request threaded irq: %d \n " , error ) ;
2014-11-09 12:36:34 -08:00
return error ;
2013-01-09 16:25:11 -08:00
}
2014-11-09 12:36:34 -08:00
/* Disable IRQ until the device is opened */
disable_irq ( client - > irq ) ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
/*
* Register the device in the input subsystem when it ' s operational .
* Otherwise , keep in this driver , so it can be be recovered or updated
* through the sysfs mode and update_fw interfaces by user or apps .
*/
if ( cyapa - > operational ) {
error = cyapa_create_input_dev ( cyapa ) ;
if ( error ) {
dev_err ( dev , " create input_dev instance failed: %d \n " ,
error ) ;
return error ;
}
2014-11-09 12:36:34 -08:00
}
2013-01-09 16:25:11 -08:00
return 0 ;
}
2014-11-02 00:03:37 -07:00
static int __maybe_unused cyapa_suspend ( struct device * dev )
2013-01-09 16:25:11 -08:00
{
2014-11-09 12:36:34 -08:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct cyapa * cyapa = i2c_get_clientdata ( client ) ;
2013-01-09 16:25:11 -08:00
u8 power_mode ;
2014-11-09 12:36:34 -08:00
int error ;
2015-01-17 18:35:26 -08:00
error = mutex_lock_interruptible ( & cyapa - > state_sync_lock ) ;
2014-11-09 12:36:34 -08:00
if ( error )
return error ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:57:42 -08:00
/*
* Runtime PM is enable only when device is in operational mode and
* users in use , so need check it before disable it to
* avoid unbalance warning .
*/
if ( pm_runtime_enabled ( dev ) )
pm_runtime_disable ( dev ) ;
2014-11-09 12:36:34 -08:00
disable_irq ( client - > irq ) ;
2013-01-09 16:25:11 -08:00
/*
* Set trackpad device to idle mode if wakeup is allowed ,
* otherwise turn off .
*/
2015-01-17 18:35:26 -08:00
if ( cyapa - > operational ) {
power_mode = device_may_wakeup ( dev ) ? cyapa - > suspend_power_mode
: PWR_MODE_OFF ;
error = cyapa - > ops - > set_power_mode ( cyapa , power_mode ,
2016-03-04 11:23:09 -08:00
cyapa - > suspend_sleep_time , CYAPA_PM_SUSPEND ) ;
2015-01-17 18:35:26 -08:00
if ( error )
dev_err ( dev , " suspend set power mode failed: %d \n " ,
error ) ;
}
2013-01-09 16:25:11 -08:00
2015-07-20 16:57:53 -07:00
/*
* Disable proximity interrupt when system idle , want true touch to
* wake the system .
*/
if ( cyapa - > dev_pwr_mode ! = PWR_MODE_OFF )
cyapa - > ops - > set_proximity ( cyapa , false ) ;
2013-01-09 16:25:11 -08:00
if ( device_may_wakeup ( dev ) )
2014-12-03 15:29:34 -08:00
cyapa - > irq_wake = ( enable_irq_wake ( client - > irq ) = = 0 ) ;
2014-11-09 12:36:34 -08:00
2015-01-17 18:35:26 -08:00
mutex_unlock ( & cyapa - > state_sync_lock ) ;
2013-01-09 16:25:11 -08:00
return 0 ;
}
2014-11-02 00:03:37 -07:00
static int __maybe_unused cyapa_resume ( struct device * dev )
2013-01-09 16:25:11 -08:00
{
2014-11-09 12:36:34 -08:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct cyapa * cyapa = i2c_get_clientdata ( client ) ;
int error ;
2015-01-17 18:35:26 -08:00
mutex_lock ( & cyapa - > state_sync_lock ) ;
2013-01-09 16:25:11 -08:00
2015-01-17 18:35:26 -08:00
if ( device_may_wakeup ( dev ) & & cyapa - > irq_wake ) {
2014-12-03 15:29:34 -08:00
disable_irq_wake ( client - > irq ) ;
2015-01-17 18:35:26 -08:00
cyapa - > irq_wake = false ;
}
2013-01-09 16:25:11 -08:00
2015-07-20 16:57:53 -07:00
/*
* Update device states and runtime PM states .
* Re - Enable proximity interrupt after enter operational mode .
*/
2015-01-17 18:35:26 -08:00
error = cyapa_reinitialize ( cyapa ) ;
2014-11-09 12:36:34 -08:00
if ( error )
2015-01-17 18:35:26 -08:00
dev_warn ( dev , " failed to reinitialize TP device: %d \n " , error ) ;
2013-01-09 16:25:11 -08:00
2014-12-03 15:29:34 -08:00
enable_irq ( client - > irq ) ;
2014-11-09 12:36:34 -08:00
2015-01-17 18:35:26 -08:00
mutex_unlock ( & cyapa - > state_sync_lock ) ;
2013-01-09 16:25:11 -08:00
return 0 ;
}
2015-01-17 18:57:42 -08:00
static int __maybe_unused cyapa_runtime_suspend ( struct device * dev )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
int error ;
error = cyapa - > ops - > set_power_mode ( cyapa ,
cyapa - > runtime_suspend_power_mode ,
2015-07-20 17:09:59 -07:00
cyapa - > runtime_suspend_sleep_time ,
2016-03-04 11:23:09 -08:00
CYAPA_PM_RUNTIME_SUSPEND ) ;
2015-01-17 18:57:42 -08:00
if ( error )
dev_warn ( dev , " runtime suspend failed: %d \n " , error ) ;
return 0 ;
}
static int __maybe_unused cyapa_runtime_resume ( struct device * dev )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
int error ;
2015-07-20 17:09:59 -07:00
error = cyapa - > ops - > set_power_mode ( cyapa ,
2016-03-04 11:23:09 -08:00
PWR_MODE_FULL_ACTIVE , 0 , CYAPA_PM_RUNTIME_RESUME ) ;
2015-01-17 18:57:42 -08:00
if ( error )
dev_warn ( dev , " runtime resume failed: %d \n " , error ) ;
return 0 ;
}
static const struct dev_pm_ops cyapa_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( cyapa_suspend , cyapa_resume )
SET_RUNTIME_PM_OPS ( cyapa_runtime_suspend , cyapa_runtime_resume , NULL )
} ;
2013-01-09 16:25:11 -08:00
static const struct i2c_device_id cyapa_id_table [ ] = {
{ " cyapa " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , cyapa_id_table ) ;
2015-01-17 22:18:59 -08:00
# ifdef CONFIG_ACPI
static const struct acpi_device_id cyapa_acpi_id [ ] = {
{ " CYAP0000 " , 0 } , /* Gen3 trackpad with 0x67 I2C address. */
{ " CYAP0001 " , 0 } , /* Gen5 trackpad with 0x24 I2C address. */
2015-07-20 17:15:46 -07:00
{ " CYAP0002 " , 0 } , /* Gen6 trackpad with 0x24 I2C address. */
2015-01-17 22:18:59 -08:00
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , cyapa_acpi_id ) ;
# endif
2015-07-30 11:24:16 -07:00
# ifdef CONFIG_OF
static const struct of_device_id cyapa_of_match [ ] = {
{ . compatible = " cypress,cyapa " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , cyapa_of_match ) ;
# endif
2013-01-09 16:25:11 -08:00
static struct i2c_driver cyapa_driver = {
. driver = {
. name = " cyapa " ,
. pm = & cyapa_pm_ops ,
2015-01-17 22:18:59 -08:00
. acpi_match_table = ACPI_PTR ( cyapa_acpi_id ) ,
2015-07-30 11:24:16 -07:00
. of_match_table = of_match_ptr ( cyapa_of_match ) ,
2013-01-09 16:25:11 -08:00
} ,
. probe = cyapa_probe ,
. id_table = cyapa_id_table ,
} ;
module_i2c_driver ( cyapa_driver ) ;
MODULE_DESCRIPTION ( " Cypress APA I2C Trackpad Driver " ) ;
MODULE_AUTHOR ( " Dudley Du <dudl@cypress.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;