2012-03-17 10:57:09 +04:00
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/input.h>
# include <linux/input/mt.h>
# include <linux/delay.h>
# include <linux/workqueue.h>
# include <linux/input/ili210x.h>
# define MAX_TOUCHES 2
# define DEFAULT_POLL_PERIOD 20
/* Touchscreen commands */
# define REG_TOUCHDATA 0x10
# define REG_PANEL_INFO 0x20
# define REG_FIRMWARE_VERSION 0x40
# define REG_CALIBRATE 0xcc
struct finger {
u8 x_low ;
u8 x_high ;
u8 y_low ;
u8 y_high ;
} __packed ;
struct touchdata {
u8 status ;
struct finger finger [ MAX_TOUCHES ] ;
} __packed ;
struct panel_info {
struct finger finger_max ;
u8 xchannel_num ;
u8 ychannel_num ;
} __packed ;
struct firmware_version {
u8 id ;
u8 major ;
u8 minor ;
} __packed ;
struct ili210x {
struct i2c_client * client ;
struct input_dev * input ;
bool ( * get_pendown_state ) ( void ) ;
unsigned int poll_period ;
struct delayed_work dwork ;
} ;
static int ili210x_read_reg ( struct i2c_client * client , u8 reg , void * buf ,
size_t len )
{
struct i2c_msg msg [ 2 ] = {
{
. addr = client - > addr ,
. flags = 0 ,
. len = 1 ,
. buf = & reg ,
} ,
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = len ,
. buf = buf ,
}
} ;
if ( i2c_transfer ( client - > adapter , msg , 2 ) ! = 2 ) {
dev_err ( & client - > dev , " i2c transfer failed \n " ) ;
return - EIO ;
}
return 0 ;
}
static void ili210x_report_events ( struct input_dev * input ,
const struct touchdata * touchdata )
{
int i ;
bool touch ;
unsigned int x , y ;
const struct finger * finger ;
for ( i = 0 ; i < MAX_TOUCHES ; i + + ) {
input_mt_slot ( input , i ) ;
finger = & touchdata - > finger [ i ] ;
touch = touchdata - > status & ( 1 < < i ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , touch ) ;
if ( touch ) {
x = finger - > x_low | ( finger - > x_high < < 8 ) ;
y = finger - > y_low | ( finger - > y_high < < 8 ) ;
input_report_abs ( input , ABS_MT_POSITION_X , x ) ;
input_report_abs ( input , ABS_MT_POSITION_Y , y ) ;
}
}
input_mt_report_pointer_emulation ( input , false ) ;
input_sync ( input ) ;
}
static bool get_pendown_state ( const struct ili210x * priv )
{
bool state = false ;
if ( priv - > get_pendown_state )
state = priv - > get_pendown_state ( ) ;
return state ;
}
static void ili210x_work ( struct work_struct * work )
{
struct ili210x * priv = container_of ( work , struct ili210x ,
dwork . work ) ;
struct i2c_client * client = priv - > client ;
struct touchdata touchdata ;
int error ;
error = ili210x_read_reg ( client , REG_TOUCHDATA ,
& touchdata , sizeof ( touchdata ) ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Unable to get touchdata, err = %d \n " , error ) ;
return ;
}
ili210x_report_events ( priv - > input , & touchdata ) ;
if ( ( touchdata . status & 0xf3 ) | | get_pendown_state ( priv ) )
schedule_delayed_work ( & priv - > dwork ,
msecs_to_jiffies ( priv - > poll_period ) ) ;
}
static irqreturn_t ili210x_irq ( int irq , void * irq_data )
{
struct ili210x * priv = irq_data ;
schedule_delayed_work ( & priv - > dwork , 0 ) ;
return IRQ_HANDLED ;
}
static ssize_t ili210x_calibrate ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct ili210x * priv = i2c_get_clientdata ( client ) ;
unsigned long calibrate ;
int rc ;
u8 cmd = REG_CALIBRATE ;
if ( kstrtoul ( buf , 10 , & calibrate ) )
return - EINVAL ;
if ( calibrate > 1 )
return - EINVAL ;
if ( calibrate ) {
rc = i2c_master_send ( priv - > client , & cmd , sizeof ( cmd ) ) ;
if ( rc ! = sizeof ( cmd ) )
return - EIO ;
}
return count ;
}
static DEVICE_ATTR ( calibrate , 0644 , NULL , ili210x_calibrate ) ;
static struct attribute * ili210x_attributes [ ] = {
& dev_attr_calibrate . attr ,
NULL ,
} ;
static const struct attribute_group ili210x_attr_group = {
. attrs = ili210x_attributes ,
} ;
2012-11-24 09:38:25 +04:00
static int ili210x_i2c_probe ( struct i2c_client * client ,
2012-03-17 10:57:09 +04:00
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
const struct ili210x_platform_data * pdata = dev - > platform_data ;
struct ili210x * priv ;
struct input_dev * input ;
struct panel_info panel ;
struct firmware_version firmware ;
int xmax , ymax ;
int error ;
dev_dbg ( dev , " Probing for ILI210X I2C Touschreen driver " ) ;
if ( ! pdata ) {
dev_err ( dev , " No platform data! \n " ) ;
return - EINVAL ;
}
if ( client - > irq < = 0 ) {
dev_err ( dev , " No IRQ! \n " ) ;
return - EINVAL ;
}
/* Get firmware version */
error = ili210x_read_reg ( client , REG_FIRMWARE_VERSION ,
& firmware , sizeof ( firmware ) ) ;
if ( error ) {
dev_err ( dev , " Failed to get firmware version, err: %d \n " ,
error ) ;
return error ;
}
/* get panel info */
error = ili210x_read_reg ( client , REG_PANEL_INFO , & panel , sizeof ( panel ) ) ;
if ( error ) {
dev_err ( dev , " Failed to get panel informations, err: %d \n " ,
error ) ;
return error ;
}
xmax = panel . finger_max . x_low | ( panel . finger_max . x_high < < 8 ) ;
ymax = panel . finger_max . y_low | ( panel . finger_max . y_high < < 8 ) ;
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
input = input_allocate_device ( ) ;
if ( ! priv | | ! input ) {
error = - ENOMEM ;
goto err_free_mem ;
}
priv - > client = client ;
priv - > input = input ;
priv - > get_pendown_state = pdata - > get_pendown_state ;
priv - > poll_period = pdata - > poll_period ? : DEFAULT_POLL_PERIOD ;
INIT_DELAYED_WORK ( & priv - > dwork , ili210x_work ) ;
/* Setup input device */
input - > name = " ILI210x Touchscreen " ;
input - > id . bustype = BUS_I2C ;
input - > dev . parent = dev ;
__set_bit ( EV_SYN , input - > evbit ) ;
__set_bit ( EV_KEY , input - > evbit ) ;
__set_bit ( EV_ABS , input - > evbit ) ;
__set_bit ( BTN_TOUCH , input - > keybit ) ;
/* Single touch */
input_set_abs_params ( input , ABS_X , 0 , xmax , 0 , 0 ) ;
input_set_abs_params ( input , ABS_Y , 0 , ymax , 0 , 0 ) ;
/* Multi touch */
2012-08-12 00:07:55 +04:00
input_mt_init_slots ( input , MAX_TOUCHES , 0 ) ;
2012-03-17 10:57:09 +04:00
input_set_abs_params ( input , ABS_MT_POSITION_X , 0 , xmax , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_POSITION_Y , 0 , ymax , 0 , 0 ) ;
input_set_drvdata ( input , priv ) ;
i2c_set_clientdata ( client , priv ) ;
error = request_irq ( client - > irq , ili210x_irq , pdata - > irq_flags ,
client - > name , priv ) ;
if ( error ) {
dev_err ( dev , " Unable to request touchscreen IRQ, err: %d \n " ,
error ) ;
goto err_free_mem ;
}
error = sysfs_create_group ( & dev - > kobj , & ili210x_attr_group ) ;
if ( error ) {
dev_err ( dev , " Unable to create sysfs attributes, err: %d \n " ,
error ) ;
goto err_free_irq ;
}
error = input_register_device ( priv - > input ) ;
if ( error ) {
dev_err ( dev , " Cannot regiser input device, err: %d \n " , error ) ;
goto err_remove_sysfs ;
}
device_init_wakeup ( & client - > dev , 1 ) ;
dev_dbg ( dev ,
" ILI210x initialized (IRQ: %d), firmware version %d.%d.%d " ,
client - > irq , firmware . id , firmware . major , firmware . minor ) ;
return 0 ;
err_remove_sysfs :
sysfs_remove_group ( & dev - > kobj , & ili210x_attr_group ) ;
err_free_irq :
free_irq ( client - > irq , priv ) ;
err_free_mem :
input_free_device ( input ) ;
kfree ( priv ) ;
return error ;
}
static int __devexit ili210x_i2c_remove ( struct i2c_client * client )
{
struct ili210x * priv = i2c_get_clientdata ( client ) ;
sysfs_remove_group ( & client - > dev . kobj , & ili210x_attr_group ) ;
free_irq ( priv - > client - > irq , priv ) ;
cancel_delayed_work_sync ( & priv - > dwork ) ;
input_unregister_device ( priv - > input ) ;
kfree ( priv ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int ili210x_i2c_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
if ( device_may_wakeup ( & client - > dev ) )
enable_irq_wake ( client - > irq ) ;
return 0 ;
}
static int ili210x_i2c_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
if ( device_may_wakeup ( & client - > dev ) )
disable_irq_wake ( client - > irq ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( ili210x_i2c_pm ,
ili210x_i2c_suspend , ili210x_i2c_resume ) ;
static const struct i2c_device_id ili210x_i2c_id [ ] = {
{ " ili210x " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ili210x_i2c_id ) ;
static struct i2c_driver ili210x_ts_driver = {
. driver = {
. name = " ili210x_i2c " ,
. owner = THIS_MODULE ,
. pm = & ili210x_i2c_pm ,
} ,
. id_table = ili210x_i2c_id ,
. probe = ili210x_i2c_probe ,
2012-11-24 09:27:39 +04:00
. remove = ili210x_i2c_remove ,
2012-03-17 10:57:09 +04:00
} ;
module_i2c_driver ( ili210x_ts_driver ) ;
MODULE_AUTHOR ( " Olivier Sobrie <olivier@sobrie.be> " ) ;
MODULE_DESCRIPTION ( " ILI210X I2C Touchscreen Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;