2015-07-21 02:53:30 +03:00
/*
* Cypress APA trackpad with I2C interface
*
* Author : Dudley Du < dudl @ cypress . com >
*
* Copyright ( C ) 2015 Cypress Semiconductor , 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/mutex.h>
# include <linux/completion.h>
# include <linux/slab.h>
# include <asm/unaligned.h>
# include <linux/crc-itu-t.h>
# include "cyapa.h"
# define GEN6_ENABLE_CMD_IRQ 0x41
# define GEN6_DISABLE_CMD_IRQ 0x42
# define GEN6_ENABLE_DEV_IRQ 0x43
# define GEN6_DISABLE_DEV_IRQ 0x44
# define GEN6_POWER_MODE_ACTIVE 0x01
# define GEN6_POWER_MODE_LP_MODE1 0x02
# define GEN6_POWER_MODE_LP_MODE2 0x03
# define GEN6_POWER_MODE_BTN_ONLY 0x04
# define GEN6_SET_POWER_MODE_INTERVAL 0x47
# define GEN6_GET_POWER_MODE_INTERVAL 0x48
# define GEN6_MAX_RX_NUM 14
# define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC 0x00
# define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM 0x12
struct pip_app_cmd_head {
__le16 addr ;
__le16 length ;
u8 report_id ;
u8 resv ; /* Reserved, must be 0 */
u8 cmd_code ; /* bit7: resv, set to 0; bit6~0: command code.*/
} __packed ;
struct pip_app_resp_head {
__le16 length ;
u8 report_id ;
u8 resv ; /* Reserved, must be 0 */
u8 cmd_code ; /* bit7: TGL; bit6~0: command code.*/
/*
* The value of data_status can be the first byte of data or
* the command status or the unsupported command code depending on the
* requested command code .
*/
u8 data_status ;
} __packed ;
struct pip_fixed_info {
u8 silicon_id_high ;
u8 silicon_id_low ;
u8 family_id ;
} ;
static u8 pip_get_bl_info [ ] = {
0x04 , 0x00 , 0x0B , 0x00 , 0x40 , 0x00 , 0x01 , 0x38 ,
0x00 , 0x00 , 0x70 , 0x9E , 0x17
} ;
static bool cyapa_sort_pip_hid_descriptor_data ( struct cyapa * cyapa ,
u8 * buf , int len )
{
if ( len ! = PIP_HID_DESCRIPTOR_SIZE )
return false ;
if ( buf [ PIP_RESP_REPORT_ID_OFFSET ] = = PIP_HID_APP_REPORT_ID | |
buf [ PIP_RESP_REPORT_ID_OFFSET ] = = PIP_HID_BL_REPORT_ID )
return true ;
return false ;
}
static int cyapa_get_pip_fixed_info ( struct cyapa * cyapa ,
struct pip_fixed_info * pip_info , bool is_bootloader )
{
u8 resp_data [ PIP_READ_SYS_INFO_RESP_LENGTH ] ;
int resp_len ;
u16 product_family ;
int error ;
if ( is_bootloader ) {
/* Read Bootloader Information to determine Gen5 or Gen6. */
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa ,
pip_get_bl_info , sizeof ( pip_get_bl_info ) ,
resp_data , & resp_len ,
2000 , cyapa_sort_tsg_pip_bl_resp_data ,
false ) ;
if ( error | | resp_len < PIP_BL_GET_INFO_RESP_LENGTH )
return error ? error : - EIO ;
pip_info - > family_id = resp_data [ 8 ] ;
pip_info - > silicon_id_low = resp_data [ 10 ] ;
pip_info - > silicon_id_high = resp_data [ 11 ] ;
return 0 ;
}
/* Get App System Information to determine Gen5 or Gen6. */
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa ,
pip_read_sys_info , PIP_READ_SYS_INFO_CMD_LENGTH ,
resp_data , & resp_len ,
2000 , cyapa_pip_sort_system_info_data , false ) ;
if ( error | | resp_len < PIP_READ_SYS_INFO_RESP_LENGTH )
return error ? error : - EIO ;
product_family = get_unaligned_le16 ( & resp_data [ 7 ] ) ;
if ( ( product_family & PIP_PRODUCT_FAMILY_MASK ) ! =
PIP_PRODUCT_FAMILY_TRACKPAD )
return - EINVAL ;
pip_info - > family_id = resp_data [ 19 ] ;
pip_info - > silicon_id_low = resp_data [ 21 ] ;
pip_info - > silicon_id_high = resp_data [ 22 ] ;
return 0 ;
}
int cyapa_pip_state_parse ( struct cyapa * cyapa , u8 * reg_data , int len )
{
u8 cmd [ ] = { 0x01 , 0x00 } ;
struct pip_fixed_info pip_info ;
u8 resp_data [ PIP_HID_DESCRIPTOR_SIZE ] ;
int resp_len ;
bool is_bootloader ;
int error ;
cyapa - > state = CYAPA_STATE_NO_DEVICE ;
/* Try to wake from it deep sleep state if it is. */
cyapa_pip_deep_sleep ( cyapa , PIP_DEEP_SLEEP_STATE_ON ) ;
/* Empty the buffer queue to get fresh data with later commands. */
cyapa_empty_pip_output_data ( cyapa , NULL , NULL , NULL ) ;
/*
* Read description info from trackpad device to determine running in
* APP mode or Bootloader mode .
*/
resp_len = PIP_HID_DESCRIPTOR_SIZE ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa ,
cmd , sizeof ( cmd ) ,
resp_data , & resp_len ,
300 ,
cyapa_sort_pip_hid_descriptor_data ,
false ) ;
if ( error )
return error ;
if ( resp_data [ PIP_RESP_REPORT_ID_OFFSET ] = = PIP_HID_BL_REPORT_ID )
is_bootloader = true ;
else if ( resp_data [ PIP_RESP_REPORT_ID_OFFSET ] = = PIP_HID_APP_REPORT_ID )
is_bootloader = false ;
else
return - EAGAIN ;
/* Get PIP fixed information to determine Gen5 or Gen6. */
memset ( & pip_info , 0 , sizeof ( struct pip_fixed_info ) ) ;
error = cyapa_get_pip_fixed_info ( cyapa , & pip_info , is_bootloader ) ;
if ( error )
return error ;
if ( pip_info . family_id = = 0x9B & & pip_info . silicon_id_high = = 0x0B ) {
cyapa - > gen = CYAPA_GEN6 ;
cyapa - > state = is_bootloader ? CYAPA_STATE_GEN6_BL
: CYAPA_STATE_GEN6_APP ;
} else if ( pip_info . family_id = = 0x91 & &
pip_info . silicon_id_high = = 0x02 ) {
cyapa - > gen = CYAPA_GEN5 ;
cyapa - > state = is_bootloader ? CYAPA_STATE_GEN5_BL
: CYAPA_STATE_GEN5_APP ;
}
return 0 ;
}
static int cyapa_gen6_read_sys_info ( struct cyapa * cyapa )
{
u8 resp_data [ PIP_READ_SYS_INFO_RESP_LENGTH ] ;
int resp_len ;
u16 product_family ;
u8 rotat_align ;
int error ;
/* Get App System Information to determine Gen5 or Gen6. */
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa ,
pip_read_sys_info , PIP_READ_SYS_INFO_CMD_LENGTH ,
resp_data , & resp_len ,
2000 , cyapa_pip_sort_system_info_data , false ) ;
if ( error | | resp_len < sizeof ( resp_data ) )
return error ? error : - EIO ;
product_family = get_unaligned_le16 ( & resp_data [ 7 ] ) ;
if ( ( product_family & PIP_PRODUCT_FAMILY_MASK ) ! =
PIP_PRODUCT_FAMILY_TRACKPAD )
return - EINVAL ;
cyapa - > platform_ver = ( resp_data [ 67 ] > > PIP_BL_PLATFORM_VER_SHIFT ) &
PIP_BL_PLATFORM_VER_MASK ;
cyapa - > fw_maj_ver = resp_data [ 9 ] ;
cyapa - > fw_min_ver = resp_data [ 10 ] ;
cyapa - > electrodes_x = resp_data [ 33 ] ;
cyapa - > electrodes_y = resp_data [ 34 ] ;
cyapa - > physical_size_x = get_unaligned_le16 ( & resp_data [ 35 ] ) / 100 ;
cyapa - > physical_size_y = get_unaligned_le16 ( & resp_data [ 37 ] ) / 100 ;
cyapa - > max_abs_x = get_unaligned_le16 ( & resp_data [ 39 ] ) ;
cyapa - > max_abs_y = get_unaligned_le16 ( & resp_data [ 41 ] ) ;
cyapa - > max_z = get_unaligned_le16 ( & resp_data [ 43 ] ) ;
cyapa - > x_origin = resp_data [ 45 ] & 0x01 ;
cyapa - > y_origin = resp_data [ 46 ] & 0x01 ;
cyapa - > btn_capability = ( resp_data [ 70 ] < < 3 ) & CAPABILITY_BTN_MASK ;
memcpy ( & cyapa - > product_id [ 0 ] , & resp_data [ 51 ] , 5 ) ;
cyapa - > product_id [ 5 ] = ' - ' ;
memcpy ( & cyapa - > product_id [ 6 ] , & resp_data [ 56 ] , 6 ) ;
cyapa - > product_id [ 12 ] = ' - ' ;
memcpy ( & cyapa - > product_id [ 13 ] , & resp_data [ 62 ] , 2 ) ;
cyapa - > product_id [ 15 ] = ' \0 ' ;
2015-10-12 20:16:16 +03:00
/* Get the number of Rx electrodes. */
2015-07-21 02:53:30 +03:00
rotat_align = resp_data [ 68 ] ;
2015-10-12 20:16:16 +03:00
cyapa - > electrodes_rx =
rotat_align ? cyapa - > electrodes_y : cyapa - > electrodes_x ;
2015-07-21 02:53:30 +03:00
cyapa - > aligned_electrodes_rx = ( cyapa - > electrodes_rx + 3 ) & ~ 3u ;
if ( ! cyapa - > electrodes_x | | ! cyapa - > electrodes_y | |
! cyapa - > physical_size_x | | ! cyapa - > physical_size_y | |
! cyapa - > max_abs_x | | ! cyapa - > max_abs_y | | ! cyapa - > max_z )
return - EINVAL ;
return 0 ;
}
static int cyapa_gen6_bl_read_app_info ( struct cyapa * cyapa )
{
u8 resp_data [ PIP_BL_APP_INFO_RESP_LENGTH ] ;
int resp_len ;
int error ;
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa ,
pip_bl_read_app_info , PIP_BL_READ_APP_INFO_CMD_LENGTH ,
resp_data , & resp_len ,
500 , cyapa_sort_tsg_pip_bl_resp_data , false ) ;
if ( error | | resp_len < PIP_BL_APP_INFO_RESP_LENGTH | |
! PIP_CMD_COMPLETE_SUCCESS ( resp_data ) )
return error ? error : - EIO ;
cyapa - > fw_maj_ver = resp_data [ 8 ] ;
cyapa - > fw_min_ver = resp_data [ 9 ] ;
cyapa - > platform_ver = ( resp_data [ 12 ] > > PIP_BL_PLATFORM_VER_SHIFT ) &
PIP_BL_PLATFORM_VER_MASK ;
memcpy ( & cyapa - > product_id [ 0 ] , & resp_data [ 13 ] , 5 ) ;
cyapa - > product_id [ 5 ] = ' - ' ;
memcpy ( & cyapa - > product_id [ 6 ] , & resp_data [ 18 ] , 6 ) ;
cyapa - > product_id [ 12 ] = ' - ' ;
memcpy ( & cyapa - > product_id [ 13 ] , & resp_data [ 24 ] , 2 ) ;
cyapa - > product_id [ 15 ] = ' \0 ' ;
return 0 ;
}
static int cyapa_gen6_config_dev_irq ( struct cyapa * cyapa , u8 cmd_code )
{
u8 cmd [ ] = { 0x04 , 0x00 , 0x05 , 0x00 , 0x2f , 0x00 , cmd_code } ;
u8 resp_data [ 6 ] ;
int resp_len ;
int error ;
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa , cmd , sizeof ( cmd ) ,
resp_data , & resp_len ,
500 , cyapa_sort_tsg_pip_app_resp_data , false ) ;
if ( error | | ! VALID_CMD_RESP_HEADER ( resp_data , cmd_code ) | |
! PIP_CMD_COMPLETE_SUCCESS ( resp_data )
)
return error < 0 ? error : - EINVAL ;
return 0 ;
}
2015-07-21 02:57:53 +03:00
static int cyapa_gen6_set_proximity ( struct cyapa * cyapa , bool enable )
{
int error ;
cyapa_gen6_config_dev_irq ( cyapa , GEN6_DISABLE_CMD_IRQ ) ;
error = cyapa_pip_set_proximity ( cyapa , enable ) ;
cyapa_gen6_config_dev_irq ( cyapa , GEN6_ENABLE_CMD_IRQ ) ;
return error ;
}
2015-07-21 02:53:30 +03:00
static int cyapa_gen6_change_power_state ( struct cyapa * cyapa , u8 power_mode )
{
u8 cmd [ ] = { 0x04 , 0x00 , 0x06 , 0x00 , 0x2f , 0x00 , 0x46 , power_mode } ;
u8 resp_data [ 6 ] ;
int resp_len ;
int error ;
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa , cmd , sizeof ( cmd ) ,
resp_data , & resp_len ,
500 , cyapa_sort_tsg_pip_app_resp_data , false ) ;
if ( error | | ! VALID_CMD_RESP_HEADER ( resp_data , 0x46 ) )
return error < 0 ? error : - EINVAL ;
/* New power state applied in device not match the set power state. */
if ( resp_data [ 5 ] ! = power_mode )
return - EAGAIN ;
return 0 ;
}
static int cyapa_gen6_set_interval_setting ( struct cyapa * cyapa ,
struct gen6_interval_setting * interval_setting )
{
struct gen6_set_interval_cmd {
__le16 addr ;
__le16 length ;
u8 report_id ;
u8 rsvd ; /* Reserved, must be 0 */
u8 cmd_code ;
__le16 active_interval ;
__le16 lp1_interval ;
__le16 lp2_interval ;
} __packed set_interval_cmd ;
u8 resp_data [ 11 ] ;
int resp_len ;
int error ;
memset ( & set_interval_cmd , 0 , sizeof ( set_interval_cmd ) ) ;
put_unaligned_le16 ( PIP_OUTPUT_REPORT_ADDR , & set_interval_cmd . addr ) ;
put_unaligned_le16 ( sizeof ( set_interval_cmd ) - 2 ,
& set_interval_cmd . length ) ;
set_interval_cmd . report_id = PIP_APP_CMD_REPORT_ID ;
set_interval_cmd . cmd_code = GEN6_SET_POWER_MODE_INTERVAL ;
put_unaligned_le16 ( interval_setting - > active_interval ,
& set_interval_cmd . active_interval ) ;
put_unaligned_le16 ( interval_setting - > lp1_interval ,
& set_interval_cmd . lp1_interval ) ;
put_unaligned_le16 ( interval_setting - > lp2_interval ,
& set_interval_cmd . lp2_interval ) ;
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa ,
( u8 * ) & set_interval_cmd , sizeof ( set_interval_cmd ) ,
resp_data , & resp_len ,
500 , cyapa_sort_tsg_pip_app_resp_data , false ) ;
if ( error | |
! VALID_CMD_RESP_HEADER ( resp_data , GEN6_SET_POWER_MODE_INTERVAL ) )
return error < 0 ? error : - EINVAL ;
/* Get the real set intervals from response. */
interval_setting - > active_interval = get_unaligned_le16 ( & resp_data [ 5 ] ) ;
interval_setting - > lp1_interval = get_unaligned_le16 ( & resp_data [ 7 ] ) ;
interval_setting - > lp2_interval = get_unaligned_le16 ( & resp_data [ 9 ] ) ;
return 0 ;
}
static int cyapa_gen6_get_interval_setting ( struct cyapa * cyapa ,
struct gen6_interval_setting * interval_setting )
{
u8 cmd [ ] = { 0x04 , 0x00 , 0x05 , 0x00 , 0x2f , 0x00 ,
GEN6_GET_POWER_MODE_INTERVAL } ;
u8 resp_data [ 11 ] ;
int resp_len ;
int error ;
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa , cmd , sizeof ( cmd ) ,
resp_data , & resp_len ,
500 , cyapa_sort_tsg_pip_app_resp_data , false ) ;
if ( error | |
! VALID_CMD_RESP_HEADER ( resp_data , GEN6_GET_POWER_MODE_INTERVAL ) )
return error < 0 ? error : - EINVAL ;
interval_setting - > active_interval = get_unaligned_le16 ( & resp_data [ 5 ] ) ;
interval_setting - > lp1_interval = get_unaligned_le16 ( & resp_data [ 7 ] ) ;
interval_setting - > lp2_interval = get_unaligned_le16 ( & resp_data [ 9 ] ) ;
return 0 ;
}
static int cyapa_gen6_deep_sleep ( struct cyapa * cyapa , u8 state )
{
u8 ping [ ] = { 0x04 , 0x00 , 0x05 , 0x00 , 0x2f , 0x00 , 0x00 } ;
if ( state = = PIP_DEEP_SLEEP_STATE_ON )
/*
* Send ping command to notify device prepare for wake up
* when it ' s in deep sleep mode . At this time , device will
* response nothing except an I2C NAK .
*/
cyapa_i2c_pip_write ( cyapa , ping , sizeof ( ping ) ) ;
return cyapa_pip_deep_sleep ( cyapa , state ) ;
}
static int cyapa_gen6_set_power_mode ( struct cyapa * cyapa ,
2016-03-04 22:23:09 +03:00
u8 power_mode , u16 sleep_time , enum cyapa_pm_stage pm_stage )
2015-07-21 02:53:30 +03:00
{
struct device * dev = & cyapa - > client - > dev ;
struct gen6_interval_setting * interval_setting =
& cyapa - > gen6_interval_setting ;
u8 lp_mode ;
int error ;
if ( cyapa - > state ! = CYAPA_STATE_GEN6_APP )
return 0 ;
if ( PIP_DEV_GET_PWR_STATE ( cyapa ) = = UNINIT_PWR_MODE ) {
/*
* Assume TP in deep sleep mode when driver is loaded ,
* avoid driver unload and reload command IO issue caused by TP
* has been set into deep sleep mode when unloading .
*/
PIP_DEV_SET_PWR_STATE ( cyapa , PWR_MODE_OFF ) ;
}
if ( PIP_DEV_UNINIT_SLEEP_TIME ( cyapa ) & &
PIP_DEV_GET_PWR_STATE ( cyapa ) ! = PWR_MODE_OFF )
PIP_DEV_SET_SLEEP_TIME ( cyapa , UNINIT_SLEEP_TIME ) ;
if ( PIP_DEV_GET_PWR_STATE ( cyapa ) = = power_mode ) {
if ( power_mode = = PWR_MODE_OFF | |
power_mode = = PWR_MODE_FULL_ACTIVE | |
power_mode = = PWR_MODE_BTN_ONLY | |
PIP_DEV_GET_SLEEP_TIME ( cyapa ) = = sleep_time ) {
/* Has in correct power mode state, early return. */
return 0 ;
}
}
if ( power_mode = = PWR_MODE_OFF ) {
cyapa_gen6_config_dev_irq ( cyapa , GEN6_DISABLE_CMD_IRQ ) ;
error = cyapa_gen6_deep_sleep ( cyapa , PIP_DEEP_SLEEP_STATE_OFF ) ;
if ( error ) {
dev_err ( dev , " enter deep sleep fail: %d \n " , error ) ;
return error ;
}
PIP_DEV_SET_PWR_STATE ( cyapa , PWR_MODE_OFF ) ;
return 0 ;
}
/*
* When trackpad in power off mode , it cannot change to other power
* state directly , must be wake up from sleep firstly , then
* continue to do next power sate change .
*/
if ( PIP_DEV_GET_PWR_STATE ( cyapa ) = = PWR_MODE_OFF ) {
error = cyapa_gen6_deep_sleep ( cyapa , PIP_DEEP_SLEEP_STATE_ON ) ;
if ( error ) {
dev_err ( dev , " deep sleep wake fail: %d \n " , error ) ;
return error ;
}
}
/*
* Disable device assert interrupts for command response to avoid
* disturbing system suspending or hibernating process .
*/
cyapa_gen6_config_dev_irq ( cyapa , GEN6_DISABLE_CMD_IRQ ) ;
if ( power_mode = = PWR_MODE_FULL_ACTIVE ) {
error = cyapa_gen6_change_power_state ( cyapa ,
GEN6_POWER_MODE_ACTIVE ) ;
if ( error ) {
dev_err ( dev , " change to active fail: %d \n " , error ) ;
goto out ;
}
PIP_DEV_SET_PWR_STATE ( cyapa , PWR_MODE_FULL_ACTIVE ) ;
/* Sync the interval setting from device. */
cyapa_gen6_get_interval_setting ( cyapa , interval_setting ) ;
} else if ( power_mode = = PWR_MODE_BTN_ONLY ) {
error = cyapa_gen6_change_power_state ( cyapa ,
GEN6_POWER_MODE_BTN_ONLY ) ;
if ( error ) {
dev_err ( dev , " fail to button only mode: %d \n " , error ) ;
goto out ;
}
PIP_DEV_SET_PWR_STATE ( cyapa , PWR_MODE_BTN_ONLY ) ;
} else {
/*
* Gen6 internally supports to 2 low power scan interval time ,
* so can help to switch power mode quickly .
* such as runtime suspend and system suspend .
*/
if ( interval_setting - > lp1_interval = = sleep_time ) {
lp_mode = GEN6_POWER_MODE_LP_MODE1 ;
} else if ( interval_setting - > lp2_interval = = sleep_time ) {
lp_mode = GEN6_POWER_MODE_LP_MODE2 ;
} else {
if ( interval_setting - > lp1_interval = = 0 ) {
interval_setting - > lp1_interval = sleep_time ;
lp_mode = GEN6_POWER_MODE_LP_MODE1 ;
} else {
interval_setting - > lp2_interval = sleep_time ;
lp_mode = GEN6_POWER_MODE_LP_MODE2 ;
}
cyapa_gen6_set_interval_setting ( cyapa ,
interval_setting ) ;
}
error = cyapa_gen6_change_power_state ( cyapa , lp_mode ) ;
if ( error ) {
dev_err ( dev , " set power state to 0x%02x failed: %d \n " ,
lp_mode , error ) ;
goto out ;
}
PIP_DEV_SET_SLEEP_TIME ( cyapa , sleep_time ) ;
PIP_DEV_SET_PWR_STATE ( cyapa ,
cyapa_sleep_time_to_pwr_cmd ( sleep_time ) ) ;
}
out :
cyapa_gen6_config_dev_irq ( cyapa , GEN6_ENABLE_CMD_IRQ ) ;
return error ;
}
static int cyapa_gen6_initialize ( struct cyapa * cyapa )
{
return 0 ;
}
static int cyapa_pip_retrieve_data_structure ( struct cyapa * cyapa ,
u16 read_offset , u16 read_len , u8 data_id ,
u8 * data , int * data_buf_lens )
{
struct retrieve_data_struct_cmd {
struct pip_app_cmd_head head ;
__le16 read_offset ;
__le16 read_length ;
u8 data_id ;
} __packed cmd ;
u8 resp_data [ GEN6_MAX_RX_NUM + 10 ] ;
int resp_len ;
int error ;
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
put_unaligned_le16 ( PIP_OUTPUT_REPORT_ADDR , & cmd . head . addr ) ;
put_unaligned_le16 ( sizeof ( cmd ) , & cmd . head . length - 2 ) ;
cmd . head . report_id = PIP_APP_CMD_REPORT_ID ;
cmd . head . cmd_code = PIP_RETRIEVE_DATA_STRUCTURE ;
put_unaligned_le16 ( read_offset , & cmd . read_offset ) ;
put_unaligned_le16 ( read_len , & cmd . read_length ) ;
cmd . data_id = data_id ;
resp_len = sizeof ( resp_data ) ;
error = cyapa_i2c_pip_cmd_irq_sync ( cyapa ,
( u8 * ) & cmd , sizeof ( cmd ) ,
resp_data , & resp_len ,
500 , cyapa_sort_tsg_pip_app_resp_data ,
true ) ;
if ( error | | ! PIP_CMD_COMPLETE_SUCCESS ( resp_data ) | |
resp_data [ 6 ] ! = data_id | |
! VALID_CMD_RESP_HEADER ( resp_data , PIP_RETRIEVE_DATA_STRUCTURE ) )
return ( error < 0 ) ? error : - EAGAIN ;
read_len = get_unaligned_le16 ( & resp_data [ 7 ] ) ;
if ( * data_buf_lens < read_len ) {
* data_buf_lens = read_len ;
return - ENOBUFS ;
}
memcpy ( data , & resp_data [ 10 ] , read_len ) ;
* data_buf_lens = read_len ;
return 0 ;
}
static ssize_t cyapa_gen6_show_baseline ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct cyapa * cyapa = dev_get_drvdata ( dev ) ;
u8 data [ GEN6_MAX_RX_NUM ] ;
int data_len ;
int size = 0 ;
int i ;
int error ;
int resume_error ;
if ( ! cyapa_is_pip_app_mode ( cyapa ) )
return - EBUSY ;
/* 1. Suspend Scanning*/
error = cyapa_pip_suspend_scanning ( cyapa ) ;
if ( error )
return error ;
/* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
data_len = sizeof ( data ) ;
error = cyapa_pip_retrieve_data_structure ( cyapa , 0 , data_len ,
GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC ,
data , & data_len ) ;
if ( error )
goto resume_scanning ;
size = scnprintf ( buf , PAGE_SIZE , " %d %d %d %d %d %d " ,
data [ 0 ] , /* RX Attenuator Mutual */
data [ 1 ] , /* IDAC Mutual */
data [ 2 ] , /* RX Attenuator Self RX */
data [ 3 ] , /* IDAC Self RX */
data [ 4 ] , /* RX Attenuator Self TX */
data [ 5 ] /* IDAC Self TX */
) ;
/* 3. Read Attenuator Trim. */
data_len = sizeof ( data ) ;
error = cyapa_pip_retrieve_data_structure ( cyapa , 0 , data_len ,
GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM ,
data , & data_len ) ;
if ( error )
goto resume_scanning ;
/* set attenuator trim values. */
for ( i = 0 ; i < data_len ; i + + )
size + = scnprintf ( buf + size , PAGE_SIZE - size , " %d " , data [ i ] ) ;
size + = scnprintf ( buf + size , PAGE_SIZE - size , " \n " ) ;
resume_scanning :
/* 4. Resume Scanning*/
resume_error = cyapa_pip_resume_scanning ( cyapa ) ;
if ( resume_error | | error ) {
memset ( buf , 0 , PAGE_SIZE ) ;
return resume_error ? resume_error : error ;
}
return size ;
}
static int cyapa_gen6_operational_check ( struct cyapa * cyapa )
{
struct device * dev = & cyapa - > client - > dev ;
int error ;
if ( cyapa - > gen ! = CYAPA_GEN6 )
return - ENODEV ;
switch ( cyapa - > state ) {
case CYAPA_STATE_GEN6_BL :
error = cyapa_pip_bl_exit ( cyapa ) ;
if ( error ) {
/* Try to update trackpad product information. */
cyapa_gen6_bl_read_app_info ( cyapa ) ;
goto out ;
}
cyapa - > state = CYAPA_STATE_GEN6_APP ;
case CYAPA_STATE_GEN6_APP :
/*
* If trackpad device in deep sleep mode ,
* the app command will fail .
* So always try to reset trackpad device to full active when
* the device state is required .
*/
error = cyapa_gen6_set_power_mode ( cyapa ,
2016-03-04 22:23:09 +03:00
PWR_MODE_FULL_ACTIVE , 0 , CYAPA_PM_ACTIVE ) ;
2015-07-21 02:53:30 +03:00
if ( error )
dev_warn ( dev , " %s: failed to set power active mode. \n " ,
__func__ ) ;
2015-07-21 02:57:53 +03:00
/* By default, the trackpad proximity function is enabled. */
error = cyapa_pip_set_proximity ( cyapa , true ) ;
if ( error )
dev_warn ( dev , " %s: failed to enable proximity. \n " ,
__func__ ) ;
2015-07-21 02:53:30 +03:00
/* Get trackpad product information. */
error = cyapa_gen6_read_sys_info ( cyapa ) ;
if ( error )
goto out ;
/* Only support product ID starting with CYTRA */
if ( memcmp ( cyapa - > product_id , product_id ,
strlen ( product_id ) ) ! = 0 ) {
dev_err ( dev , " %s: unknown product ID (%s) \n " ,
__func__ , cyapa - > product_id ) ;
error = - EINVAL ;
}
break ;
default :
error = - EINVAL ;
}
out :
return error ;
}
const struct cyapa_dev_ops cyapa_gen6_ops = {
. check_fw = cyapa_pip_check_fw ,
. bl_enter = cyapa_pip_bl_enter ,
. bl_initiate = cyapa_pip_bl_initiate ,
. update_fw = cyapa_pip_do_fw_update ,
. bl_activate = cyapa_pip_bl_activate ,
. bl_deactivate = cyapa_pip_bl_deactivate ,
. show_baseline = cyapa_gen6_show_baseline ,
. calibrate_store = cyapa_pip_do_calibrate ,
. initialize = cyapa_gen6_initialize ,
. state_parse = cyapa_pip_state_parse ,
. operational_check = cyapa_gen6_operational_check ,
. irq_handler = cyapa_pip_irq_handler ,
. irq_cmd_handler = cyapa_pip_irq_cmd_handler ,
. sort_empty_output_data = cyapa_empty_pip_output_data ,
. set_power_mode = cyapa_gen6_set_power_mode ,
2015-07-21 02:57:53 +03:00
. set_proximity = cyapa_gen6_set_proximity ,
2015-07-21 02:53:30 +03:00
} ;