2012-03-26 04:23:19 +04:00
/*
* Wacom Penabled Driver for I2C
*
2013-06-19 10:13:28 +04:00
* Copyright ( c ) 2011 - 2013 Tatsunosuke Tobita , Wacom .
2012-03-26 04:23:19 +04:00
* < tobita . tatsunosuke @ wacom . co . jp >
*
* This program is free software ; you can redistribute it
* and / or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation ; either version of 2 of the License ,
* or ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/input.h>
# include <linux/i2c.h>
# include <linux/slab.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/gpio.h>
# include <asm/unaligned.h>
# define WACOM_CMD_QUERY0 0x04
# define WACOM_CMD_QUERY1 0x00
# define WACOM_CMD_QUERY2 0x33
# define WACOM_CMD_QUERY3 0x02
# define WACOM_CMD_THROW0 0x05
# define WACOM_CMD_THROW1 0x00
# define WACOM_QUERY_SIZE 19
struct wacom_features {
int x_max ;
int y_max ;
int pressure_max ;
char fw_version ;
} ;
struct wacom_i2c {
struct i2c_client * client ;
struct input_dev * input ;
u8 data [ WACOM_QUERY_SIZE ] ;
2013-06-19 10:13:28 +04:00
bool prox ;
int tool ;
2012-03-26 04:23:19 +04:00
} ;
static int wacom_query_device ( struct i2c_client * client ,
struct wacom_features * features )
{
int ret ;
u8 cmd1 [ ] = { WACOM_CMD_QUERY0 , WACOM_CMD_QUERY1 ,
WACOM_CMD_QUERY2 , WACOM_CMD_QUERY3 } ;
u8 cmd2 [ ] = { WACOM_CMD_THROW0 , WACOM_CMD_THROW1 } ;
u8 data [ WACOM_QUERY_SIZE ] ;
struct i2c_msg msgs [ ] = {
{
. addr = client - > addr ,
. flags = 0 ,
. len = sizeof ( cmd1 ) ,
. buf = cmd1 ,
} ,
{
. addr = client - > addr ,
. flags = 0 ,
. len = sizeof ( cmd2 ) ,
. buf = cmd2 ,
} ,
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = sizeof ( data ) ,
. buf = data ,
} ,
} ;
ret = i2c_transfer ( client - > adapter , msgs , ARRAY_SIZE ( msgs ) ) ;
if ( ret < 0 )
return ret ;
if ( ret ! = ARRAY_SIZE ( msgs ) )
return - EIO ;
features - > x_max = get_unaligned_le16 ( & data [ 3 ] ) ;
features - > y_max = get_unaligned_le16 ( & data [ 5 ] ) ;
features - > pressure_max = get_unaligned_le16 ( & data [ 11 ] ) ;
features - > fw_version = get_unaligned_le16 ( & data [ 13 ] ) ;
dev_dbg ( & client - > dev ,
" x_max:%d, y_max:%d, pressure:%d, fw:%d \n " ,
features - > x_max , features - > y_max ,
features - > pressure_max , features - > fw_version ) ;
return 0 ;
}
static irqreturn_t wacom_i2c_irq ( int irq , void * dev_id )
{
struct wacom_i2c * wac_i2c = dev_id ;
struct input_dev * input = wac_i2c - > input ;
u8 * data = wac_i2c - > data ;
unsigned int x , y , pressure ;
unsigned char tsw , f1 , f2 , ers ;
int error ;
2012-04-10 11:21:09 +04:00
error = i2c_master_recv ( wac_i2c - > client ,
wac_i2c - > data , sizeof ( wac_i2c - > data ) ) ;
if ( error < 0 )
2012-03-26 04:23:19 +04:00
goto out ;
tsw = data [ 3 ] & 0x01 ;
ers = data [ 3 ] & 0x04 ;
f1 = data [ 3 ] & 0x02 ;
f2 = data [ 3 ] & 0x10 ;
x = le16_to_cpup ( ( __le16 * ) & data [ 4 ] ) ;
y = le16_to_cpup ( ( __le16 * ) & data [ 6 ] ) ;
pressure = le16_to_cpup ( ( __le16 * ) & data [ 8 ] ) ;
2013-06-19 10:13:28 +04:00
if ( ! wac_i2c - > prox )
wac_i2c - > tool = ( data [ 3 ] & 0x0c ) ?
BTN_TOOL_RUBBER : BTN_TOOL_PEN ;
wac_i2c - > prox = data [ 3 ] & 0x20 ;
2012-03-26 04:23:19 +04:00
input_report_key ( input , BTN_TOUCH , tsw | | ers ) ;
2013-06-19 10:13:28 +04:00
input_report_key ( input , wac_i2c - > tool , wac_i2c - > prox ) ;
2012-03-26 04:23:19 +04:00
input_report_key ( input , BTN_STYLUS , f1 ) ;
input_report_key ( input , BTN_STYLUS2 , f2 ) ;
input_report_abs ( input , ABS_X , x ) ;
input_report_abs ( input , ABS_Y , y ) ;
input_report_abs ( input , ABS_PRESSURE , pressure ) ;
input_sync ( input ) ;
out :
return IRQ_HANDLED ;
}
static int wacom_i2c_open ( struct input_dev * dev )
{
struct wacom_i2c * wac_i2c = input_get_drvdata ( dev ) ;
struct i2c_client * client = wac_i2c - > client ;
enable_irq ( client - > irq ) ;
return 0 ;
}
static void wacom_i2c_close ( struct input_dev * dev )
{
struct wacom_i2c * wac_i2c = input_get_drvdata ( dev ) ;
struct i2c_client * client = wac_i2c - > client ;
disable_irq ( client - > irq ) ;
}
2012-11-24 09:38:25 +04:00
static int wacom_i2c_probe ( struct i2c_client * client ,
2012-03-26 04:23:19 +04:00
const struct i2c_device_id * id )
{
struct wacom_i2c * wac_i2c ;
struct input_dev * input ;
2012-07-08 03:40:16 +04:00
struct wacom_features features = { 0 } ;
2012-03-26 04:23:19 +04:00
int error ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( & client - > dev , " i2c_check_functionality error \n " ) ;
return - EIO ;
}
error = wacom_query_device ( client , & features ) ;
if ( error )
return error ;
wac_i2c = kzalloc ( sizeof ( * wac_i2c ) , GFP_KERNEL ) ;
input = input_allocate_device ( ) ;
if ( ! wac_i2c | | ! input ) {
error = - ENOMEM ;
goto err_free_mem ;
}
wac_i2c - > client = client ;
wac_i2c - > input = input ;
input - > name = " Wacom I2C Digitizer " ;
input - > id . bustype = BUS_I2C ;
input - > id . vendor = 0x56a ;
input - > id . version = features . fw_version ;
input - > dev . parent = & client - > dev ;
input - > open = wacom_i2c_open ;
input - > close = wacom_i2c_close ;
input - > evbit [ 0 ] | = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_ABS ) ;
__set_bit ( BTN_TOOL_PEN , input - > keybit ) ;
__set_bit ( BTN_TOOL_RUBBER , input - > keybit ) ;
__set_bit ( BTN_STYLUS , input - > keybit ) ;
__set_bit ( BTN_STYLUS2 , input - > keybit ) ;
__set_bit ( BTN_TOUCH , input - > keybit ) ;
input_set_abs_params ( input , ABS_X , 0 , features . x_max , 0 , 0 ) ;
input_set_abs_params ( input , ABS_Y , 0 , features . y_max , 0 , 0 ) ;
input_set_abs_params ( input , ABS_PRESSURE ,
0 , features . pressure_max , 0 , 0 ) ;
input_set_drvdata ( input , wac_i2c ) ;
error = request_threaded_irq ( client - > irq , NULL , wacom_i2c_irq ,
2012-04-10 11:21:09 +04:00
IRQF_TRIGGER_LOW | IRQF_ONESHOT ,
2012-03-26 04:23:19 +04:00
" wacom_i2c " , wac_i2c ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Failed to enable IRQ, error: %d \n " , error ) ;
goto err_free_mem ;
}
/* Disable the IRQ, we'll enable it in wac_i2c_open() */
disable_irq ( client - > irq ) ;
error = input_register_device ( wac_i2c - > input ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Failed to register input device, error: %d \n " , error ) ;
goto err_free_irq ;
}
i2c_set_clientdata ( client , wac_i2c ) ;
return 0 ;
err_free_irq :
free_irq ( client - > irq , wac_i2c ) ;
err_free_mem :
input_free_device ( input ) ;
kfree ( wac_i2c ) ;
return error ;
}
2012-11-24 09:50:47 +04:00
static int wacom_i2c_remove ( struct i2c_client * client )
2012-03-26 04:23:19 +04:00
{
struct wacom_i2c * wac_i2c = i2c_get_clientdata ( client ) ;
free_irq ( client - > irq , wac_i2c ) ;
input_unregister_device ( wac_i2c - > input ) ;
kfree ( wac_i2c ) ;
return 0 ;
}
2014-11-02 10:04:14 +03:00
static int __maybe_unused wacom_i2c_suspend ( struct device * dev )
2012-03-26 04:23:19 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
disable_irq ( client - > irq ) ;
return 0 ;
}
2014-11-02 10:04:14 +03:00
static int __maybe_unused wacom_i2c_resume ( struct device * dev )
2012-03-26 04:23:19 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
enable_irq ( client - > irq ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( wacom_i2c_pm , wacom_i2c_suspend , wacom_i2c_resume ) ;
static const struct i2c_device_id wacom_i2c_id [ ] = {
{ " WAC_I2C_EMR " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , wacom_i2c_id ) ;
static struct i2c_driver wacom_i2c_driver = {
. driver = {
. name = " wacom_i2c " ,
. pm = & wacom_i2c_pm ,
} ,
. probe = wacom_i2c_probe ,
2012-11-24 09:27:39 +04:00
. remove = wacom_i2c_remove ,
2012-03-26 04:23:19 +04:00
. id_table = wacom_i2c_id ,
} ;
module_i2c_driver ( wacom_i2c_driver ) ;
MODULE_AUTHOR ( " Tatsunosuke Tobita <tobita.tatsunosuke@wacom.co.jp> " ) ;
MODULE_DESCRIPTION ( " WACOM EMR I2C Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;