2010-03-22 09:23:24 +03:00
/*
2010-03-22 09:23:29 +03:00
* AD714X CapTouch Programmable Controller driver supporting AD7142 / 3 / 7 / 8 / 7 A
2010-03-22 09:23:24 +03:00
*
* Copyright 2009 Analog Devices Inc .
*
* Licensed under the GPL - 2 or later .
*/
# include <linux/device.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/input/ad714x.h>
# include "ad714x.h"
# define AD714X_PWR_CTRL 0x0
# define AD714X_STG_CAL_EN_REG 0x1
# define AD714X_AMB_COMP_CTRL0_REG 0x2
# define AD714X_PARTID_REG 0x17
# define AD7142_PARTID 0xE620
2010-03-22 09:23:29 +03:00
# define AD7143_PARTID 0xE630
# define AD7147_PARTID 0x1470
# define AD7148_PARTID 0x1480
2010-03-22 09:23:24 +03:00
# define AD714X_STAGECFG_REG 0x80
# define AD714X_SYSCFG_REG 0x0
# define STG_LOW_INT_EN_REG 0x5
# define STG_HIGH_INT_EN_REG 0x6
# define STG_COM_INT_EN_REG 0x7
# define STG_LOW_INT_STA_REG 0x8
# define STG_HIGH_INT_STA_REG 0x9
# define STG_COM_INT_STA_REG 0xA
# define CDC_RESULT_S0 0xB
# define CDC_RESULT_S1 0xC
# define CDC_RESULT_S2 0xD
# define CDC_RESULT_S3 0xE
# define CDC_RESULT_S4 0xF
# define CDC_RESULT_S5 0x10
# define CDC_RESULT_S6 0x11
# define CDC_RESULT_S7 0x12
# define CDC_RESULT_S8 0x13
# define CDC_RESULT_S9 0x14
# define CDC_RESULT_S10 0x15
# define CDC_RESULT_S11 0x16
# define STAGE0_AMBIENT 0xF1
# define STAGE1_AMBIENT 0x115
# define STAGE2_AMBIENT 0x139
# define STAGE3_AMBIENT 0x15D
# define STAGE4_AMBIENT 0x181
# define STAGE5_AMBIENT 0x1A5
# define STAGE6_AMBIENT 0x1C9
# define STAGE7_AMBIENT 0x1ED
# define STAGE8_AMBIENT 0x211
# define STAGE9_AMBIENT 0x234
# define STAGE10_AMBIENT 0x259
# define STAGE11_AMBIENT 0x27D
# define PER_STAGE_REG_NUM 36
# define STAGE_NUM 12
# define STAGE_CFGREG_NUM 8
# define SYS_CFGREG_NUM 8
/*
* driver information which will be used to maintain the software flow
*/
enum ad714x_device_state { IDLE , JITTER , ACTIVE , SPACE } ;
struct ad714x_slider_drv {
int highest_stage ;
int abs_pos ;
int flt_pos ;
enum ad714x_device_state state ;
struct input_dev * input ;
} ;
struct ad714x_wheel_drv {
int abs_pos ;
int flt_pos ;
int pre_mean_value ;
int pre_highest_stage ;
int pre_mean_value_no_offset ;
int mean_value ;
int mean_value_no_offset ;
int pos_offset ;
int pos_ratio ;
int highest_stage ;
enum ad714x_device_state state ;
struct input_dev * input ;
} ;
struct ad714x_touchpad_drv {
int x_highest_stage ;
int x_flt_pos ;
int x_abs_pos ;
int y_highest_stage ;
int y_flt_pos ;
int y_abs_pos ;
int left_ep ;
int left_ep_val ;
int right_ep ;
int right_ep_val ;
int top_ep ;
int top_ep_val ;
int bottom_ep ;
int bottom_ep_val ;
enum ad714x_device_state state ;
struct input_dev * input ;
} ;
struct ad714x_button_drv {
enum ad714x_device_state state ;
/*
* Unlike slider / wheel / touchpad , all buttons point to
* same input_dev instance
*/
struct input_dev * input ;
} ;
struct ad714x_driver_data {
struct ad714x_slider_drv * slider ;
struct ad714x_wheel_drv * wheel ;
struct ad714x_touchpad_drv * touchpad ;
struct ad714x_button_drv * button ;
} ;
/*
* information to integrate all things which will be private data
* of spi / i2c device
*/
struct ad714x_chip {
unsigned short h_state ;
unsigned short l_state ;
unsigned short c_state ;
unsigned short adc_reg [ STAGE_NUM ] ;
unsigned short amb_reg [ STAGE_NUM ] ;
unsigned short sensor_val [ STAGE_NUM ] ;
struct ad714x_platform_data * hw ;
struct ad714x_driver_data * sw ;
int irq ;
struct device * dev ;
ad714x_read_t read ;
ad714x_write_t write ;
struct mutex mutex ;
unsigned product ;
unsigned version ;
} ;
static void ad714x_use_com_int ( struct ad714x_chip * ad714x ,
int start_stage , int end_stage )
{
unsigned short data ;
unsigned short mask ;
mask = ( ( 1 < < ( end_stage + 1 ) ) - 1 ) - ( 1 < < start_stage ) ;
ad714x - > read ( ad714x - > dev , STG_COM_INT_EN_REG , & data ) ;
data | = 1 < < start_stage ;
ad714x - > write ( ad714x - > dev , STG_COM_INT_EN_REG , data ) ;
ad714x - > read ( ad714x - > dev , STG_HIGH_INT_EN_REG , & data ) ;
data & = ~ mask ;
ad714x - > write ( ad714x - > dev , STG_HIGH_INT_EN_REG , data ) ;
}
static void ad714x_use_thr_int ( struct ad714x_chip * ad714x ,
int start_stage , int end_stage )
{
unsigned short data ;
unsigned short mask ;
mask = ( ( 1 < < ( end_stage + 1 ) ) - 1 ) - ( 1 < < start_stage ) ;
ad714x - > read ( ad714x - > dev , STG_COM_INT_EN_REG , & data ) ;
data & = ~ ( 1 < < start_stage ) ;
ad714x - > write ( ad714x - > dev , STG_COM_INT_EN_REG , data ) ;
ad714x - > read ( ad714x - > dev , STG_HIGH_INT_EN_REG , & data ) ;
data | = mask ;
ad714x - > write ( ad714x - > dev , STG_HIGH_INT_EN_REG , data ) ;
}
static int ad714x_cal_highest_stage ( struct ad714x_chip * ad714x ,
int start_stage , int end_stage )
{
int max_res = 0 ;
int max_idx = 0 ;
int i ;
for ( i = start_stage ; i < = end_stage ; i + + ) {
if ( ad714x - > sensor_val [ i ] > max_res ) {
max_res = ad714x - > sensor_val [ i ] ;
max_idx = i ;
}
}
return max_idx ;
}
static int ad714x_cal_abs_pos ( struct ad714x_chip * ad714x ,
int start_stage , int end_stage ,
int highest_stage , int max_coord )
{
int a_param , b_param ;
if ( highest_stage = = start_stage ) {
a_param = ad714x - > sensor_val [ start_stage + 1 ] ;
b_param = ad714x - > sensor_val [ start_stage ] +
ad714x - > sensor_val [ start_stage + 1 ] ;
} else if ( highest_stage = = end_stage ) {
a_param = ad714x - > sensor_val [ end_stage ] *
( end_stage - start_stage ) +
ad714x - > sensor_val [ end_stage - 1 ] *
( end_stage - start_stage - 1 ) ;
b_param = ad714x - > sensor_val [ end_stage ] +
ad714x - > sensor_val [ end_stage - 1 ] ;
} else {
a_param = ad714x - > sensor_val [ highest_stage ] *
( highest_stage - start_stage ) +
ad714x - > sensor_val [ highest_stage - 1 ] *
( highest_stage - start_stage - 1 ) +
ad714x - > sensor_val [ highest_stage + 1 ] *
( highest_stage - start_stage + 1 ) ;
b_param = ad714x - > sensor_val [ highest_stage ] +
ad714x - > sensor_val [ highest_stage - 1 ] +
ad714x - > sensor_val [ highest_stage + 1 ] ;
}
return ( max_coord / ( end_stage - start_stage ) ) * a_param / b_param ;
}
/*
* One button can connect to multi positive and negative of CDCs
* Multi - buttons can connect to same positive / negative of one CDC
*/
static void ad714x_button_state_machine ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_button_plat * hw = & ad714x - > hw - > button [ idx ] ;
struct ad714x_button_drv * sw = & ad714x - > sw - > button [ idx ] ;
switch ( sw - > state ) {
case IDLE :
if ( ( ( ad714x - > h_state & hw - > h_mask ) = = hw - > h_mask ) & &
( ( ad714x - > l_state & hw - > l_mask ) = = hw - > l_mask ) ) {
dev_dbg ( ad714x - > dev , " button %d touched \n " , idx ) ;
input_report_key ( sw - > input , hw - > keycode , 1 ) ;
input_sync ( sw - > input ) ;
sw - > state = ACTIVE ;
}
break ;
case ACTIVE :
if ( ( ( ad714x - > h_state & hw - > h_mask ) ! = hw - > h_mask ) | |
( ( ad714x - > l_state & hw - > l_mask ) ! = hw - > l_mask ) ) {
dev_dbg ( ad714x - > dev , " button %d released \n " , idx ) ;
input_report_key ( sw - > input , hw - > keycode , 0 ) ;
input_sync ( sw - > input ) ;
sw - > state = IDLE ;
}
break ;
default :
break ;
}
}
/*
* The response of a sensor is defined by the absolute number of codes
* between the current CDC value and the ambient value .
*/
static void ad714x_slider_cal_sensor_val ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_slider_plat * hw = & ad714x - > hw - > slider [ idx ] ;
int i ;
for ( i = hw - > start_stage ; i < = hw - > end_stage ; i + + ) {
ad714x - > read ( ad714x - > dev , CDC_RESULT_S0 + i ,
& ad714x - > adc_reg [ i ] ) ;
ad714x - > read ( ad714x - > dev ,
STAGE0_AMBIENT + i * PER_STAGE_REG_NUM ,
& ad714x - > amb_reg [ i ] ) ;
ad714x - > sensor_val [ i ] = abs ( ad714x - > adc_reg [ i ] -
ad714x - > amb_reg [ i ] ) ;
}
}
static void ad714x_slider_cal_highest_stage ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_slider_plat * hw = & ad714x - > hw - > slider [ idx ] ;
struct ad714x_slider_drv * sw = & ad714x - > sw - > slider [ idx ] ;
sw - > highest_stage = ad714x_cal_highest_stage ( ad714x , hw - > start_stage ,
hw - > end_stage ) ;
dev_dbg ( ad714x - > dev , " slider %d highest_stage:%d \n " , idx ,
sw - > highest_stage ) ;
}
/*
* The formulae are very straight forward . It uses the sensor with the
* highest response and the 2 adjacent ones .
* When Sensor 0 has the highest response , only sensor 0 and sensor 1
* are used in the calculations . Similarly when the last sensor has the
* highest response , only the last sensor and the second last sensors
* are used in the calculations .
*
* For i = idx_of_peak_Sensor - 1 to i = idx_of_peak_Sensor + 1
* v + = Sensor response ( i ) * i
* w + = Sensor response ( i )
* POS = ( Number_of_Positions_Wanted / ( Number_of_Sensors_Used - 1 ) ) * ( v / w )
*/
static void ad714x_slider_cal_abs_pos ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_slider_plat * hw = & ad714x - > hw - > slider [ idx ] ;
struct ad714x_slider_drv * sw = & ad714x - > sw - > slider [ idx ] ;
sw - > abs_pos = ad714x_cal_abs_pos ( ad714x , hw - > start_stage , hw - > end_stage ,
sw - > highest_stage , hw - > max_coord ) ;
dev_dbg ( ad714x - > dev , " slider %d absolute position:%d \n " , idx ,
sw - > abs_pos ) ;
}
/*
* To minimise the Impact of the noise on the algorithm , ADI developed a
* routine that filters the CDC results after they have been read by the
* host processor .
* The filter used is an Infinite Input Response ( IIR ) filter implemented
* in firmware and attenuates the noise on the CDC results after they ' ve
* been read by the host processor .
* Filtered_CDC_result = ( Filtered_CDC_result * ( 10 - Coefficient ) +
* Latest_CDC_result * Coefficient ) / 10
*/
static void ad714x_slider_cal_flt_pos ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_slider_drv * sw = & ad714x - > sw - > slider [ idx ] ;
sw - > flt_pos = ( sw - > flt_pos * ( 10 - 4 ) +
sw - > abs_pos * 4 ) / 10 ;
dev_dbg ( ad714x - > dev , " slider %d filter position:%d \n " , idx ,
sw - > flt_pos ) ;
}
static void ad714x_slider_use_com_int ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_slider_plat * hw = & ad714x - > hw - > slider [ idx ] ;
ad714x_use_com_int ( ad714x , hw - > start_stage , hw - > end_stage ) ;
}
static void ad714x_slider_use_thr_int ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_slider_plat * hw = & ad714x - > hw - > slider [ idx ] ;
ad714x_use_thr_int ( ad714x , hw - > start_stage , hw - > end_stage ) ;
}
static void ad714x_slider_state_machine ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_slider_plat * hw = & ad714x - > hw - > slider [ idx ] ;
struct ad714x_slider_drv * sw = & ad714x - > sw - > slider [ idx ] ;
unsigned short h_state , c_state ;
unsigned short mask ;
mask = ( ( 1 < < ( hw - > end_stage + 1 ) ) - 1 ) - ( ( 1 < < hw - > start_stage ) - 1 ) ;
h_state = ad714x - > h_state & mask ;
c_state = ad714x - > c_state & mask ;
switch ( sw - > state ) {
case IDLE :
if ( h_state ) {
sw - > state = JITTER ;
/* In End of Conversion interrupt mode, the AD714X
* continuously generates hardware interrupts .
*/
ad714x_slider_use_com_int ( ad714x , idx ) ;
dev_dbg ( ad714x - > dev , " slider %d touched \n " , idx ) ;
}
break ;
case JITTER :
if ( c_state = = mask ) {
ad714x_slider_cal_sensor_val ( ad714x , idx ) ;
ad714x_slider_cal_highest_stage ( ad714x , idx ) ;
ad714x_slider_cal_abs_pos ( ad714x , idx ) ;
sw - > flt_pos = sw - > abs_pos ;
sw - > state = ACTIVE ;
}
break ;
case ACTIVE :
if ( c_state = = mask ) {
if ( h_state ) {
ad714x_slider_cal_sensor_val ( ad714x , idx ) ;
ad714x_slider_cal_highest_stage ( ad714x , idx ) ;
ad714x_slider_cal_abs_pos ( ad714x , idx ) ;
ad714x_slider_cal_flt_pos ( ad714x , idx ) ;
input_report_abs ( sw - > input , ABS_X , sw - > flt_pos ) ;
input_report_key ( sw - > input , BTN_TOUCH , 1 ) ;
} else {
/* When the user lifts off the sensor, configure
* the AD714X back to threshold interrupt mode .
*/
ad714x_slider_use_thr_int ( ad714x , idx ) ;
sw - > state = IDLE ;
input_report_key ( sw - > input , BTN_TOUCH , 0 ) ;
dev_dbg ( ad714x - > dev , " slider %d released \n " ,
idx ) ;
}
input_sync ( sw - > input ) ;
}
break ;
default :
break ;
}
}
/*
* When the scroll wheel is activated , we compute the absolute position based
* on the sensor values . To calculate the position , we first determine the
* sensor that has the greatest response among the 8 sensors that constitutes
* the scrollwheel . Then we determined the 2 sensors on either sides of the
* sensor with the highest response and we apply weights to these sensors .
*/
static void ad714x_wheel_cal_highest_stage ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_wheel_plat * hw = & ad714x - > hw - > wheel [ idx ] ;
struct ad714x_wheel_drv * sw = & ad714x - > sw - > wheel [ idx ] ;
sw - > pre_highest_stage = sw - > highest_stage ;
sw - > highest_stage = ad714x_cal_highest_stage ( ad714x , hw - > start_stage ,
hw - > end_stage ) ;
dev_dbg ( ad714x - > dev , " wheel %d highest_stage:%d \n " , idx ,
sw - > highest_stage ) ;
}
static void ad714x_wheel_cal_sensor_val ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_wheel_plat * hw = & ad714x - > hw - > wheel [ idx ] ;
int i ;
for ( i = hw - > start_stage ; i < = hw - > end_stage ; i + + ) {
ad714x - > read ( ad714x - > dev , CDC_RESULT_S0 + i ,
& ad714x - > adc_reg [ i ] ) ;
ad714x - > read ( ad714x - > dev ,
STAGE0_AMBIENT + i * PER_STAGE_REG_NUM ,
& ad714x - > amb_reg [ i ] ) ;
if ( ad714x - > adc_reg [ i ] > ad714x - > amb_reg [ i ] )
ad714x - > sensor_val [ i ] = ad714x - > adc_reg [ i ] -
ad714x - > amb_reg [ i ] ;
else
ad714x - > sensor_val [ i ] = 0 ;
}
}
/*
* When the scroll wheel is activated , we compute the absolute position based
* on the sensor values . To calculate the position , we first determine the
* sensor that has the greatest response among the 8 sensors that constitutes
* the scrollwheel . Then we determined the 2 sensors on either sides of the
* sensor with the highest response and we apply weights to these sensors . The
* result of this computation gives us the mean value which defined by the
* following formula :
* For i = second_before_highest_stage to i = second_after_highest_stage
* v + = Sensor response ( i ) * WEIGHT * ( i + 3 )
* w + = Sensor response ( i )
* Mean_Value = v / w
* pos_on_scrollwheel = ( Mean_Value - position_offset ) / position_ratio
*/
# define WEIGHT_FACTOR 30
/* This constant prevents the "PositionOffset" from reaching a big value */
# define OFFSET_POSITION_CLAMP 120
static void ad714x_wheel_cal_abs_pos ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_wheel_plat * hw = & ad714x - > hw - > wheel [ idx ] ;
struct ad714x_wheel_drv * sw = & ad714x - > sw - > wheel [ idx ] ;
int stage_num = hw - > end_stage - hw - > start_stage + 1 ;
int second_before , first_before , highest , first_after , second_after ;
int a_param , b_param ;
/* Calculate Mean value */
second_before = ( sw - > highest_stage + stage_num - 2 ) % stage_num ;
first_before = ( sw - > highest_stage + stage_num - 1 ) % stage_num ;
highest = sw - > highest_stage ;
first_after = ( sw - > highest_stage + stage_num + 1 ) % stage_num ;
second_after = ( sw - > highest_stage + stage_num + 2 ) % stage_num ;
if ( ( ( sw - > highest_stage - hw - > start_stage ) > 1 ) & &
( ( hw - > end_stage - sw - > highest_stage ) > 1 ) ) {
a_param = ad714x - > sensor_val [ second_before ] *
( second_before - hw - > start_stage + 3 ) +
ad714x - > sensor_val [ first_before ] *
( second_before - hw - > start_stage + 3 ) +
ad714x - > sensor_val [ highest ] *
( second_before - hw - > start_stage + 3 ) +
ad714x - > sensor_val [ first_after ] *
( first_after - hw - > start_stage + 3 ) +
ad714x - > sensor_val [ second_after ] *
( second_after - hw - > start_stage + 3 ) ;
} else {
a_param = ad714x - > sensor_val [ second_before ] *
( second_before - hw - > start_stage + 1 ) +
ad714x - > sensor_val [ first_before ] *
( second_before - hw - > start_stage + 2 ) +
ad714x - > sensor_val [ highest ] *
( second_before - hw - > start_stage + 3 ) +
ad714x - > sensor_val [ first_after ] *
( first_after - hw - > start_stage + 4 ) +
ad714x - > sensor_val [ second_after ] *
( second_after - hw - > start_stage + 5 ) ;
}
a_param * = WEIGHT_FACTOR ;
b_param = ad714x - > sensor_val [ second_before ] +
ad714x - > sensor_val [ first_before ] +
ad714x - > sensor_val [ highest ] +
ad714x - > sensor_val [ first_after ] +
ad714x - > sensor_val [ second_after ] ;
sw - > pre_mean_value = sw - > mean_value ;
sw - > mean_value = a_param / b_param ;
/* Calculate the offset */
if ( ( sw - > pre_highest_stage = = hw - > end_stage ) & &
( sw - > highest_stage = = hw - > start_stage ) )
sw - > pos_offset = sw - > mean_value ;
else if ( ( sw - > pre_highest_stage = = hw - > start_stage ) & &
( sw - > highest_stage = = hw - > end_stage ) )
sw - > pos_offset = sw - > pre_mean_value ;
if ( sw - > pos_offset > OFFSET_POSITION_CLAMP )
sw - > pos_offset = OFFSET_POSITION_CLAMP ;
/* Calculate the mean value without the offset */
sw - > pre_mean_value_no_offset = sw - > mean_value_no_offset ;
sw - > mean_value_no_offset = sw - > mean_value - sw - > pos_offset ;
if ( sw - > mean_value_no_offset < 0 )
sw - > mean_value_no_offset = 0 ;
/* Calculate ratio to scale down to NUMBER_OF_WANTED_POSITIONS */
if ( ( sw - > pre_highest_stage = = hw - > end_stage ) & &
( sw - > highest_stage = = hw - > start_stage ) )
sw - > pos_ratio = ( sw - > pre_mean_value_no_offset * 100 ) /
hw - > max_coord ;
else if ( ( sw - > pre_highest_stage = = hw - > start_stage ) & &
( sw - > highest_stage = = hw - > end_stage ) )
sw - > pos_ratio = ( sw - > mean_value_no_offset * 100 ) /
hw - > max_coord ;
sw - > abs_pos = ( sw - > mean_value_no_offset * 100 ) / sw - > pos_ratio ;
if ( sw - > abs_pos > hw - > max_coord )
sw - > abs_pos = hw - > max_coord ;
}
static void ad714x_wheel_cal_flt_pos ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_wheel_plat * hw = & ad714x - > hw - > wheel [ idx ] ;
struct ad714x_wheel_drv * sw = & ad714x - > sw - > wheel [ idx ] ;
if ( ( ( sw - > pre_highest_stage = = hw - > end_stage ) & &
( sw - > highest_stage = = hw - > start_stage ) ) | |
( ( sw - > pre_highest_stage = = hw - > start_stage ) & &
( sw - > highest_stage = = hw - > end_stage ) ) )
sw - > flt_pos = sw - > abs_pos ;
else
sw - > flt_pos = ( ( sw - > flt_pos * 30 ) + ( sw - > abs_pos * 71 ) ) / 100 ;
if ( sw - > flt_pos > hw - > max_coord )
sw - > flt_pos = hw - > max_coord ;
}
static void ad714x_wheel_use_com_int ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_wheel_plat * hw = & ad714x - > hw - > wheel [ idx ] ;
ad714x_use_com_int ( ad714x , hw - > start_stage , hw - > end_stage ) ;
}
static void ad714x_wheel_use_thr_int ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_wheel_plat * hw = & ad714x - > hw - > wheel [ idx ] ;
ad714x_use_thr_int ( ad714x , hw - > start_stage , hw - > end_stage ) ;
}
static void ad714x_wheel_state_machine ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_wheel_plat * hw = & ad714x - > hw - > wheel [ idx ] ;
struct ad714x_wheel_drv * sw = & ad714x - > sw - > wheel [ idx ] ;
unsigned short h_state , c_state ;
unsigned short mask ;
mask = ( ( 1 < < ( hw - > end_stage + 1 ) ) - 1 ) - ( ( 1 < < hw - > start_stage ) - 1 ) ;
h_state = ad714x - > h_state & mask ;
c_state = ad714x - > c_state & mask ;
switch ( sw - > state ) {
case IDLE :
if ( h_state ) {
sw - > state = JITTER ;
/* In End of Conversion interrupt mode, the AD714X
* continuously generates hardware interrupts .
*/
ad714x_wheel_use_com_int ( ad714x , idx ) ;
dev_dbg ( ad714x - > dev , " wheel %d touched \n " , idx ) ;
}
break ;
case JITTER :
if ( c_state = = mask ) {
ad714x_wheel_cal_sensor_val ( ad714x , idx ) ;
ad714x_wheel_cal_highest_stage ( ad714x , idx ) ;
ad714x_wheel_cal_abs_pos ( ad714x , idx ) ;
sw - > flt_pos = sw - > abs_pos ;
sw - > state = ACTIVE ;
}
break ;
case ACTIVE :
if ( c_state = = mask ) {
if ( h_state ) {
ad714x_wheel_cal_sensor_val ( ad714x , idx ) ;
ad714x_wheel_cal_highest_stage ( ad714x , idx ) ;
ad714x_wheel_cal_abs_pos ( ad714x , idx ) ;
ad714x_wheel_cal_flt_pos ( ad714x , idx ) ;
input_report_abs ( sw - > input , ABS_WHEEL ,
sw - > abs_pos ) ;
input_report_key ( sw - > input , BTN_TOUCH , 1 ) ;
} else {
/* When the user lifts off the sensor, configure
* the AD714X back to threshold interrupt mode .
*/
ad714x_wheel_use_thr_int ( ad714x , idx ) ;
sw - > state = IDLE ;
input_report_key ( sw - > input , BTN_TOUCH , 0 ) ;
dev_dbg ( ad714x - > dev , " wheel %d released \n " ,
idx ) ;
}
input_sync ( sw - > input ) ;
}
break ;
default :
break ;
}
}
static void touchpad_cal_sensor_val ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
int i ;
for ( i = hw - > x_start_stage ; i < = hw - > x_end_stage ; i + + ) {
ad714x - > read ( ad714x - > dev , CDC_RESULT_S0 + i ,
& ad714x - > adc_reg [ i ] ) ;
ad714x - > read ( ad714x - > dev ,
STAGE0_AMBIENT + i * PER_STAGE_REG_NUM ,
& ad714x - > amb_reg [ i ] ) ;
if ( ad714x - > adc_reg [ i ] > ad714x - > amb_reg [ i ] )
ad714x - > sensor_val [ i ] = ad714x - > adc_reg [ i ] -
ad714x - > amb_reg [ i ] ;
else
ad714x - > sensor_val [ i ] = 0 ;
}
}
static void touchpad_cal_highest_stage ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
struct ad714x_touchpad_drv * sw = & ad714x - > sw - > touchpad [ idx ] ;
sw - > x_highest_stage = ad714x_cal_highest_stage ( ad714x ,
hw - > x_start_stage , hw - > x_end_stage ) ;
sw - > y_highest_stage = ad714x_cal_highest_stage ( ad714x ,
hw - > y_start_stage , hw - > y_end_stage ) ;
dev_dbg ( ad714x - > dev ,
" touchpad %d x_highest_stage:%d, y_highest_stage:%d \n " ,
idx , sw - > x_highest_stage , sw - > y_highest_stage ) ;
}
/*
* If 2 fingers are touching the sensor then 2 peaks can be observed in the
* distribution .
* The arithmetic doesn ' t support to get absolute coordinates for multi - touch
* yet .
*/
static int touchpad_check_second_peak ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
struct ad714x_touchpad_drv * sw = & ad714x - > sw - > touchpad [ idx ] ;
int i ;
for ( i = hw - > x_start_stage ; i < sw - > x_highest_stage ; i + + ) {
if ( ( ad714x - > sensor_val [ i ] - ad714x - > sensor_val [ i + 1 ] )
> ( ad714x - > sensor_val [ i + 1 ] / 10 ) )
return 1 ;
}
for ( i = sw - > x_highest_stage ; i < hw - > x_end_stage ; i + + ) {
if ( ( ad714x - > sensor_val [ i + 1 ] - ad714x - > sensor_val [ i ] )
> ( ad714x - > sensor_val [ i ] / 10 ) )
return 1 ;
}
for ( i = hw - > y_start_stage ; i < sw - > y_highest_stage ; i + + ) {
if ( ( ad714x - > sensor_val [ i ] - ad714x - > sensor_val [ i + 1 ] )
> ( ad714x - > sensor_val [ i + 1 ] / 10 ) )
return 1 ;
}
for ( i = sw - > y_highest_stage ; i < hw - > y_end_stage ; i + + ) {
if ( ( ad714x - > sensor_val [ i + 1 ] - ad714x - > sensor_val [ i ] )
> ( ad714x - > sensor_val [ i ] / 10 ) )
return 1 ;
}
return 0 ;
}
/*
* If only one finger is used to activate the touch pad then only 1 peak will be
* registered in the distribution . This peak and the 2 adjacent sensors will be
* used in the calculation of the absolute position . This will prevent hand
* shadows to affect the absolute position calculation .
*/
static void touchpad_cal_abs_pos ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
struct ad714x_touchpad_drv * sw = & ad714x - > sw - > touchpad [ idx ] ;
sw - > x_abs_pos = ad714x_cal_abs_pos ( ad714x , hw - > x_start_stage ,
hw - > x_end_stage , sw - > x_highest_stage , hw - > x_max_coord ) ;
sw - > y_abs_pos = ad714x_cal_abs_pos ( ad714x , hw - > y_start_stage ,
hw - > y_end_stage , sw - > y_highest_stage , hw - > y_max_coord ) ;
dev_dbg ( ad714x - > dev , " touchpad %d absolute position:(%d, %d) \n " , idx ,
sw - > x_abs_pos , sw - > y_abs_pos ) ;
}
static void touchpad_cal_flt_pos ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_drv * sw = & ad714x - > sw - > touchpad [ idx ] ;
sw - > x_flt_pos = ( sw - > x_flt_pos * ( 10 - 4 ) +
sw - > x_abs_pos * 4 ) / 10 ;
sw - > y_flt_pos = ( sw - > y_flt_pos * ( 10 - 4 ) +
sw - > y_abs_pos * 4 ) / 10 ;
dev_dbg ( ad714x - > dev , " touchpad %d filter position:(%d, %d) \n " ,
idx , sw - > x_flt_pos , sw - > y_flt_pos ) ;
}
/*
* To prevent distortion from showing in the absolute position , it is
* necessary to detect the end points . When endpoints are detected , the
* driver stops updating the status variables with absolute positions .
* End points are detected on the 4 edges of the touchpad sensor . The
* method to detect them is the same for all 4.
* To detect the end points , the firmware computes the difference in
* percent between the sensor on the edge and the adjacent one . The
* difference is calculated in percent in order to make the end point
* detection independent of the pressure .
*/
# define LEFT_END_POINT_DETECTION_LEVEL 550
# define RIGHT_END_POINT_DETECTION_LEVEL 750
# define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL 850
# define TOP_END_POINT_DETECTION_LEVEL 550
# define BOTTOM_END_POINT_DETECTION_LEVEL 950
# define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL 700
static int touchpad_check_endpoint ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
struct ad714x_touchpad_drv * sw = & ad714x - > sw - > touchpad [ idx ] ;
int percent_sensor_diff ;
/* left endpoint detect */
percent_sensor_diff = ( ad714x - > sensor_val [ hw - > x_start_stage ] -
ad714x - > sensor_val [ hw - > x_start_stage + 1 ] ) * 100 /
ad714x - > sensor_val [ hw - > x_start_stage + 1 ] ;
if ( ! sw - > left_ep ) {
if ( percent_sensor_diff > = LEFT_END_POINT_DETECTION_LEVEL ) {
sw - > left_ep = 1 ;
sw - > left_ep_val =
ad714x - > sensor_val [ hw - > x_start_stage + 1 ] ;
}
} else {
if ( ( percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL ) & &
( ad714x - > sensor_val [ hw - > x_start_stage + 1 ] >
LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw - > left_ep_val ) )
sw - > left_ep = 0 ;
}
/* right endpoint detect */
percent_sensor_diff = ( ad714x - > sensor_val [ hw - > x_end_stage ] -
ad714x - > sensor_val [ hw - > x_end_stage - 1 ] ) * 100 /
ad714x - > sensor_val [ hw - > x_end_stage - 1 ] ;
if ( ! sw - > right_ep ) {
if ( percent_sensor_diff > = RIGHT_END_POINT_DETECTION_LEVEL ) {
sw - > right_ep = 1 ;
sw - > right_ep_val =
ad714x - > sensor_val [ hw - > x_end_stage - 1 ] ;
}
} else {
if ( ( percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL ) & &
( ad714x - > sensor_val [ hw - > x_end_stage - 1 ] >
LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw - > right_ep_val ) )
sw - > right_ep = 0 ;
}
/* top endpoint detect */
percent_sensor_diff = ( ad714x - > sensor_val [ hw - > y_start_stage ] -
ad714x - > sensor_val [ hw - > y_start_stage + 1 ] ) * 100 /
ad714x - > sensor_val [ hw - > y_start_stage + 1 ] ;
if ( ! sw - > top_ep ) {
if ( percent_sensor_diff > = TOP_END_POINT_DETECTION_LEVEL ) {
sw - > top_ep = 1 ;
sw - > top_ep_val =
ad714x - > sensor_val [ hw - > y_start_stage + 1 ] ;
}
} else {
if ( ( percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL ) & &
( ad714x - > sensor_val [ hw - > y_start_stage + 1 ] >
TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw - > top_ep_val ) )
sw - > top_ep = 0 ;
}
/* bottom endpoint detect */
percent_sensor_diff = ( ad714x - > sensor_val [ hw - > y_end_stage ] -
ad714x - > sensor_val [ hw - > y_end_stage - 1 ] ) * 100 /
ad714x - > sensor_val [ hw - > y_end_stage - 1 ] ;
if ( ! sw - > bottom_ep ) {
if ( percent_sensor_diff > = BOTTOM_END_POINT_DETECTION_LEVEL ) {
sw - > bottom_ep = 1 ;
sw - > bottom_ep_val =
ad714x - > sensor_val [ hw - > y_end_stage - 1 ] ;
}
} else {
if ( ( percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL ) & &
( ad714x - > sensor_val [ hw - > y_end_stage - 1 ] >
TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw - > bottom_ep_val ) )
sw - > bottom_ep = 0 ;
}
return sw - > left_ep | | sw - > right_ep | | sw - > top_ep | | sw - > bottom_ep ;
}
static void touchpad_use_com_int ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
ad714x_use_com_int ( ad714x , hw - > x_start_stage , hw - > x_end_stage ) ;
}
static void touchpad_use_thr_int ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
ad714x_use_thr_int ( ad714x , hw - > x_start_stage , hw - > x_end_stage ) ;
ad714x_use_thr_int ( ad714x , hw - > y_start_stage , hw - > y_end_stage ) ;
}
static void ad714x_touchpad_state_machine ( struct ad714x_chip * ad714x , int idx )
{
struct ad714x_touchpad_plat * hw = & ad714x - > hw - > touchpad [ idx ] ;
struct ad714x_touchpad_drv * sw = & ad714x - > sw - > touchpad [ idx ] ;
unsigned short h_state , c_state ;
unsigned short mask ;
mask = ( ( ( 1 < < ( hw - > x_end_stage + 1 ) ) - 1 ) -
( ( 1 < < hw - > x_start_stage ) - 1 ) ) +
( ( ( 1 < < ( hw - > y_end_stage + 1 ) ) - 1 ) -
( ( 1 < < hw - > y_start_stage ) - 1 ) ) ;
h_state = ad714x - > h_state & mask ;
c_state = ad714x - > c_state & mask ;
switch ( sw - > state ) {
case IDLE :
if ( h_state ) {
sw - > state = JITTER ;
/* In End of Conversion interrupt mode, the AD714X
* continuously generates hardware interrupts .
*/
touchpad_use_com_int ( ad714x , idx ) ;
dev_dbg ( ad714x - > dev , " touchpad %d touched \n " , idx ) ;
}
break ;
case JITTER :
if ( c_state = = mask ) {
touchpad_cal_sensor_val ( ad714x , idx ) ;
touchpad_cal_highest_stage ( ad714x , idx ) ;
if ( ( ! touchpad_check_second_peak ( ad714x , idx ) ) & &
( ! touchpad_check_endpoint ( ad714x , idx ) ) ) {
dev_dbg ( ad714x - > dev ,
" touchpad%d, 2 fingers or endpoint \n " ,
idx ) ;
touchpad_cal_abs_pos ( ad714x , idx ) ;
sw - > x_flt_pos = sw - > x_abs_pos ;
sw - > y_flt_pos = sw - > y_abs_pos ;
sw - > state = ACTIVE ;
}
}
break ;
case ACTIVE :
if ( c_state = = mask ) {
if ( h_state ) {
touchpad_cal_sensor_val ( ad714x , idx ) ;
touchpad_cal_highest_stage ( ad714x , idx ) ;
if ( ( ! touchpad_check_second_peak ( ad714x , idx ) )
& & ( ! touchpad_check_endpoint ( ad714x , idx ) ) ) {
touchpad_cal_abs_pos ( ad714x , idx ) ;
touchpad_cal_flt_pos ( ad714x , idx ) ;
input_report_abs ( sw - > input , ABS_X ,
sw - > x_flt_pos ) ;
input_report_abs ( sw - > input , ABS_Y ,
sw - > y_flt_pos ) ;
input_report_key ( sw - > input , BTN_TOUCH ,
1 ) ;
}
} else {
/* When the user lifts off the sensor, configure
* the AD714X back to threshold interrupt mode .
*/
touchpad_use_thr_int ( ad714x , idx ) ;
sw - > state = IDLE ;
input_report_key ( sw - > input , BTN_TOUCH , 0 ) ;
dev_dbg ( ad714x - > dev , " touchpad %d released \n " ,
idx ) ;
}
input_sync ( sw - > input ) ;
}
break ;
default :
break ;
}
}
static int ad714x_hw_detect ( struct ad714x_chip * ad714x )
{
unsigned short data ;
ad714x - > read ( ad714x - > dev , AD714X_PARTID_REG , & data ) ;
switch ( data & 0xFFF0 ) {
2010-03-22 09:23:29 +03:00
case AD7142_PARTID :
ad714x - > product = 0x7142 ;
ad714x - > version = data & 0xF ;
dev_info ( ad714x - > dev , " found AD7142 captouch, rev:%d \n " ,
ad714x - > version ) ;
return 0 ;
case AD7143_PARTID :
ad714x - > product = 0x7143 ;
ad714x - > version = data & 0xF ;
dev_info ( ad714x - > dev , " found AD7143 captouch, rev:%d \n " ,
ad714x - > version ) ;
return 0 ;
2010-03-22 09:23:24 +03:00
case AD7147_PARTID :
ad714x - > product = 0x7147 ;
ad714x - > version = data & 0xF ;
2010-03-22 09:23:29 +03:00
dev_info ( ad714x - > dev , " found AD7147(A) captouch, rev:%d \n " ,
2010-03-22 09:23:24 +03:00
ad714x - > version ) ;
return 0 ;
2010-03-22 09:23:29 +03:00
case AD7148_PARTID :
ad714x - > product = 0x7148 ;
2010-03-22 09:23:24 +03:00
ad714x - > version = data & 0xF ;
2010-03-22 09:23:29 +03:00
dev_info ( ad714x - > dev , " found AD7148 captouch, rev:%d \n " ,
2010-03-22 09:23:24 +03:00
ad714x - > version ) ;
return 0 ;
default :
dev_err ( ad714x - > dev ,
" fail to detect AD714X captouch, read ID is %04x \n " ,
data ) ;
return - ENODEV ;
}
}
static void ad714x_hw_init ( struct ad714x_chip * ad714x )
{
int i , j ;
unsigned short reg_base ;
unsigned short data ;
/* configuration CDC and interrupts */
for ( i = 0 ; i < STAGE_NUM ; i + + ) {
reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM ;
for ( j = 0 ; j < STAGE_CFGREG_NUM ; j + + )
ad714x - > write ( ad714x - > dev , reg_base + j ,
ad714x - > hw - > stage_cfg_reg [ i ] [ j ] ) ;
}
for ( i = 0 ; i < SYS_CFGREG_NUM ; i + + )
ad714x - > write ( ad714x - > dev , AD714X_SYSCFG_REG + i ,
ad714x - > hw - > sys_cfg_reg [ i ] ) ;
for ( i = 0 ; i < SYS_CFGREG_NUM ; i + + )
ad714x - > read ( ad714x - > dev , AD714X_SYSCFG_REG + i ,
& data ) ;
ad714x - > write ( ad714x - > dev , AD714X_STG_CAL_EN_REG , 0xFFF ) ;
/* clear all interrupts */
ad714x - > read ( ad714x - > dev , STG_LOW_INT_STA_REG , & data ) ;
ad714x - > read ( ad714x - > dev , STG_HIGH_INT_STA_REG , & data ) ;
ad714x - > read ( ad714x - > dev , STG_COM_INT_STA_REG , & data ) ;
}
static irqreturn_t ad714x_interrupt_thread ( int irq , void * data )
{
struct ad714x_chip * ad714x = data ;
int i ;
mutex_lock ( & ad714x - > mutex ) ;
ad714x - > read ( ad714x - > dev , STG_LOW_INT_STA_REG , & ad714x - > l_state ) ;
ad714x - > read ( ad714x - > dev , STG_HIGH_INT_STA_REG , & ad714x - > h_state ) ;
ad714x - > read ( ad714x - > dev , STG_COM_INT_STA_REG , & ad714x - > c_state ) ;
for ( i = 0 ; i < ad714x - > hw - > button_num ; i + + )
ad714x_button_state_machine ( ad714x , i ) ;
for ( i = 0 ; i < ad714x - > hw - > slider_num ; i + + )
ad714x_slider_state_machine ( ad714x , i ) ;
for ( i = 0 ; i < ad714x - > hw - > wheel_num ; i + + )
ad714x_wheel_state_machine ( ad714x , i ) ;
for ( i = 0 ; i < ad714x - > hw - > touchpad_num ; i + + )
ad714x_touchpad_state_machine ( ad714x , i ) ;
mutex_unlock ( & ad714x - > mutex ) ;
return IRQ_HANDLED ;
}
# define MAX_DEVICE_NUM 8
struct ad714x_chip * ad714x_probe ( struct device * dev , u16 bus_type , int irq ,
ad714x_read_t read , ad714x_write_t write )
{
int i , alloc_idx ;
int error ;
struct input_dev * input [ MAX_DEVICE_NUM ] ;
struct ad714x_platform_data * plat_data = dev - > platform_data ;
struct ad714x_chip * ad714x ;
void * drv_mem ;
struct ad714x_button_drv * bt_drv ;
struct ad714x_slider_drv * sd_drv ;
struct ad714x_wheel_drv * wl_drv ;
struct ad714x_touchpad_drv * tp_drv ;
if ( irq < = 0 ) {
dev_err ( dev , " IRQ not configured! \n " ) ;
error = - EINVAL ;
goto err_out ;
}
if ( dev - > platform_data = = NULL ) {
dev_err ( dev , " platform data for ad714x doesn't exist \n " ) ;
error = - EINVAL ;
goto err_out ;
}
ad714x = kzalloc ( sizeof ( * ad714x ) + sizeof ( * ad714x - > sw ) +
sizeof ( * sd_drv ) * plat_data - > slider_num +
sizeof ( * wl_drv ) * plat_data - > wheel_num +
sizeof ( * tp_drv ) * plat_data - > touchpad_num +
sizeof ( * bt_drv ) * plat_data - > button_num , GFP_KERNEL ) ;
if ( ! ad714x ) {
error = - ENOMEM ;
goto err_out ;
}
ad714x - > hw = plat_data ;
drv_mem = ad714x + 1 ;
ad714x - > sw = drv_mem ;
drv_mem + = sizeof ( * ad714x - > sw ) ;
ad714x - > sw - > slider = sd_drv = drv_mem ;
drv_mem + = sizeof ( * sd_drv ) * ad714x - > hw - > slider_num ;
ad714x - > sw - > wheel = wl_drv = drv_mem ;
drv_mem + = sizeof ( * wl_drv ) * ad714x - > hw - > wheel_num ;
ad714x - > sw - > touchpad = tp_drv = drv_mem ;
drv_mem + = sizeof ( * tp_drv ) * ad714x - > hw - > touchpad_num ;
ad714x - > sw - > button = bt_drv = drv_mem ;
drv_mem + = sizeof ( * bt_drv ) * ad714x - > hw - > button_num ;
ad714x - > read = read ;
ad714x - > write = write ;
ad714x - > irq = irq ;
ad714x - > dev = dev ;
error = ad714x_hw_detect ( ad714x ) ;
if ( error )
goto err_free_mem ;
2010-06-11 14:17:00 +04:00
/* initialize and request sw/hw resources */
2010-03-22 09:23:24 +03:00
ad714x_hw_init ( ad714x ) ;
mutex_init ( & ad714x - > mutex ) ;
/*
* Allocate and register AD714X input device
*/
alloc_idx = 0 ;
/* a slider uses one input_dev instance */
if ( ad714x - > hw - > slider_num > 0 ) {
struct ad714x_slider_plat * sd_plat = ad714x - > hw - > slider ;
for ( i = 0 ; i < ad714x - > hw - > slider_num ; i + + ) {
sd_drv [ i ] . input = input [ alloc_idx ] = input_allocate_device ( ) ;
if ( ! input [ alloc_idx ] ) {
error = - ENOMEM ;
goto err_free_dev ;
}
__set_bit ( EV_ABS , input [ alloc_idx ] - > evbit ) ;
__set_bit ( EV_KEY , input [ alloc_idx ] - > evbit ) ;
__set_bit ( ABS_X , input [ alloc_idx ] - > absbit ) ;
__set_bit ( BTN_TOUCH , input [ alloc_idx ] - > keybit ) ;
input_set_abs_params ( input [ alloc_idx ] ,
ABS_X , 0 , sd_plat - > max_coord , 0 , 0 ) ;
input [ alloc_idx ] - > id . bustype = bus_type ;
input [ alloc_idx ] - > id . product = ad714x - > product ;
input [ alloc_idx ] - > id . version = ad714x - > version ;
error = input_register_device ( input [ alloc_idx ] ) ;
if ( error )
goto err_free_dev ;
alloc_idx + + ;
}
}
/* a wheel uses one input_dev instance */
if ( ad714x - > hw - > wheel_num > 0 ) {
struct ad714x_wheel_plat * wl_plat = ad714x - > hw - > wheel ;
for ( i = 0 ; i < ad714x - > hw - > wheel_num ; i + + ) {
wl_drv [ i ] . input = input [ alloc_idx ] = input_allocate_device ( ) ;
if ( ! input [ alloc_idx ] ) {
error = - ENOMEM ;
goto err_free_dev ;
}
__set_bit ( EV_KEY , input [ alloc_idx ] - > evbit ) ;
__set_bit ( EV_ABS , input [ alloc_idx ] - > evbit ) ;
__set_bit ( ABS_WHEEL , input [ alloc_idx ] - > absbit ) ;
__set_bit ( BTN_TOUCH , input [ alloc_idx ] - > keybit ) ;
input_set_abs_params ( input [ alloc_idx ] ,
ABS_WHEEL , 0 , wl_plat - > max_coord , 0 , 0 ) ;
input [ alloc_idx ] - > id . bustype = bus_type ;
input [ alloc_idx ] - > id . product = ad714x - > product ;
input [ alloc_idx ] - > id . version = ad714x - > version ;
error = input_register_device ( input [ alloc_idx ] ) ;
if ( error )
goto err_free_dev ;
alloc_idx + + ;
}
}
/* a touchpad uses one input_dev instance */
if ( ad714x - > hw - > touchpad_num > 0 ) {
struct ad714x_touchpad_plat * tp_plat = ad714x - > hw - > touchpad ;
for ( i = 0 ; i < ad714x - > hw - > touchpad_num ; i + + ) {
tp_drv [ i ] . input = input [ alloc_idx ] = input_allocate_device ( ) ;
if ( ! input [ alloc_idx ] ) {
error = - ENOMEM ;
goto err_free_dev ;
}
__set_bit ( EV_ABS , input [ alloc_idx ] - > evbit ) ;
__set_bit ( EV_KEY , input [ alloc_idx ] - > evbit ) ;
__set_bit ( ABS_X , input [ alloc_idx ] - > absbit ) ;
__set_bit ( ABS_Y , input [ alloc_idx ] - > absbit ) ;
__set_bit ( BTN_TOUCH , input [ alloc_idx ] - > keybit ) ;
input_set_abs_params ( input [ alloc_idx ] ,
ABS_X , 0 , tp_plat - > x_max_coord , 0 , 0 ) ;
input_set_abs_params ( input [ alloc_idx ] ,
ABS_Y , 0 , tp_plat - > y_max_coord , 0 , 0 ) ;
input [ alloc_idx ] - > id . bustype = bus_type ;
input [ alloc_idx ] - > id . product = ad714x - > product ;
input [ alloc_idx ] - > id . version = ad714x - > version ;
error = input_register_device ( input [ alloc_idx ] ) ;
if ( error )
goto err_free_dev ;
alloc_idx + + ;
}
}
/* all buttons use one input node */
if ( ad714x - > hw - > button_num > 0 ) {
struct ad714x_button_plat * bt_plat = ad714x - > hw - > button ;
input [ alloc_idx ] = input_allocate_device ( ) ;
if ( ! input [ alloc_idx ] ) {
error = - ENOMEM ;
goto err_free_dev ;
}
__set_bit ( EV_KEY , input [ alloc_idx ] - > evbit ) ;
for ( i = 0 ; i < ad714x - > hw - > button_num ; i + + ) {
bt_drv [ i ] . input = input [ alloc_idx ] ;
__set_bit ( bt_plat [ i ] . keycode , input [ alloc_idx ] - > keybit ) ;
}
input [ alloc_idx ] - > id . bustype = bus_type ;
input [ alloc_idx ] - > id . product = ad714x - > product ;
input [ alloc_idx ] - > id . version = ad714x - > version ;
error = input_register_device ( input [ alloc_idx ] ) ;
if ( error )
goto err_free_dev ;
alloc_idx + + ;
}
error = request_threaded_irq ( ad714x - > irq , NULL , ad714x_interrupt_thread ,
IRQF_TRIGGER_FALLING , " ad714x_captouch " , ad714x ) ;
if ( error ) {
dev_err ( dev , " can't allocate irq %d \n " , ad714x - > irq ) ;
goto err_unreg_dev ;
}
return ad714x ;
err_free_dev :
dev_err ( dev , " failed to setup AD714x input device %i \n " , alloc_idx ) ;
input_free_device ( input [ alloc_idx ] ) ;
err_unreg_dev :
while ( - - alloc_idx > = 0 )
input_unregister_device ( input [ alloc_idx ] ) ;
err_free_mem :
kfree ( ad714x ) ;
err_out :
return ERR_PTR ( error ) ;
}
EXPORT_SYMBOL ( ad714x_probe ) ;
void ad714x_remove ( struct ad714x_chip * ad714x )
{
struct ad714x_platform_data * hw = ad714x - > hw ;
struct ad714x_driver_data * sw = ad714x - > sw ;
int i ;
free_irq ( ad714x - > irq , ad714x ) ;
/* unregister and free all input devices */
for ( i = 0 ; i < hw - > slider_num ; i + + )
input_unregister_device ( sw - > slider [ i ] . input ) ;
for ( i = 0 ; i < hw - > wheel_num ; i + + )
input_unregister_device ( sw - > wheel [ i ] . input ) ;
for ( i = 0 ; i < hw - > touchpad_num ; i + + )
input_unregister_device ( sw - > touchpad [ i ] . input ) ;
if ( hw - > button_num )
input_unregister_device ( sw - > button [ 0 ] . input ) ;
kfree ( ad714x ) ;
}
EXPORT_SYMBOL ( ad714x_remove ) ;
# ifdef CONFIG_PM
int ad714x_disable ( struct ad714x_chip * ad714x )
{
unsigned short data ;
dev_dbg ( ad714x - > dev , " %s enter \n " , __func__ ) ;
mutex_lock ( & ad714x - > mutex ) ;
data = ad714x - > hw - > sys_cfg_reg [ AD714X_PWR_CTRL ] | 0x3 ;
ad714x - > write ( ad714x - > dev , AD714X_PWR_CTRL , data ) ;
mutex_unlock ( & ad714x - > mutex ) ;
return 0 ;
}
EXPORT_SYMBOL ( ad714x_disable ) ;
int ad714x_enable ( struct ad714x_chip * ad714x )
{
unsigned short data ;
dev_dbg ( ad714x - > dev , " %s enter \n " , __func__ ) ;
mutex_lock ( & ad714x - > mutex ) ;
/* resume to non-shutdown mode */
ad714x - > write ( ad714x - > dev , AD714X_PWR_CTRL ,
ad714x - > hw - > sys_cfg_reg [ AD714X_PWR_CTRL ] ) ;
/* make sure the interrupt output line is not low level after resume,
* otherwise we will get no chance to enter falling - edge irq again
*/
ad714x - > read ( ad714x - > dev , STG_LOW_INT_STA_REG , & data ) ;
ad714x - > read ( ad714x - > dev , STG_HIGH_INT_STA_REG , & data ) ;
ad714x - > read ( ad714x - > dev , STG_COM_INT_STA_REG , & data ) ;
mutex_unlock ( & ad714x - > mutex ) ;
return 0 ;
}
EXPORT_SYMBOL ( ad714x_enable ) ;
# endif
MODULE_DESCRIPTION ( " Analog Devices AD714X Capacitance Touch Sensor Driver " ) ;
MODULE_AUTHOR ( " Barry Song <21cnbao@gmail.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;