2 * Wacom Penabled Driver for I2C
4 * Copyright (c) 2011 Tatsunosuke Tobita, Wacom.
5 * <tobita.tatsunosuke@wacom.co.jp>
7 * This program is free software; you can redistribute it
8 * and/or modify it under the terms of the GNU General
9 * Public License as published by the Free Software
10 * Foundation; either version of 2 of the License,
11 * or (at your option) any later version.
14 #include <linux/module.h>
15 #include <linux/input.h>
16 #include <linux/i2c.h>
17 #include <linux/slab.h>
18 #include <linux/irq.h>
19 #include <linux/interrupt.h>
20 #include <linux/gpio.h>
21 #include <asm/unaligned.h>
23 #define WACOM_CMD_QUERY0 0x04
24 #define WACOM_CMD_QUERY1 0x00
25 #define WACOM_CMD_QUERY2 0x33
26 #define WACOM_CMD_QUERY3 0x02
27 #define WACOM_CMD_THROW0 0x05
28 #define WACOM_CMD_THROW1 0x00
29 #define WACOM_QUERY_SIZE 19
30 #define WACOM_RETRY_CNT 100
32 struct wacom_features
{
40 struct i2c_client
*client
;
41 struct input_dev
*input
;
42 u8 data
[WACOM_QUERY_SIZE
];
45 static int wacom_query_device(struct i2c_client
*client
,
46 struct wacom_features
*features
)
49 u8 cmd1
[] = { WACOM_CMD_QUERY0
, WACOM_CMD_QUERY1
,
50 WACOM_CMD_QUERY2
, WACOM_CMD_QUERY3
};
51 u8 cmd2
[] = { WACOM_CMD_THROW0
, WACOM_CMD_THROW1
};
52 u8 data
[WACOM_QUERY_SIZE
];
53 struct i2c_msg msgs
[] = {
74 ret
= i2c_transfer(client
->adapter
, msgs
, ARRAY_SIZE(msgs
));
77 if (ret
!= ARRAY_SIZE(msgs
))
80 features
->x_max
= get_unaligned_le16(&data
[3]);
81 features
->y_max
= get_unaligned_le16(&data
[5]);
82 features
->pressure_max
= get_unaligned_le16(&data
[11]);
83 features
->fw_version
= get_unaligned_le16(&data
[13]);
86 "x_max:%d, y_max:%d, pressure:%d, fw:%d\n",
87 features
->x_max
, features
->y_max
,
88 features
->pressure_max
, features
->fw_version
);
93 static irqreturn_t
wacom_i2c_irq(int irq
, void *dev_id
)
95 struct wacom_i2c
*wac_i2c
= dev_id
;
96 struct input_dev
*input
= wac_i2c
->input
;
97 u8
*data
= wac_i2c
->data
;
98 unsigned int x
, y
, pressure
;
99 unsigned char tsw
, f1
, f2
, ers
;
102 error
= i2c_master_recv(wac_i2c
->client
,
103 wac_i2c
->data
, sizeof(wac_i2c
->data
));
107 tsw
= data
[3] & 0x01;
108 ers
= data
[3] & 0x04;
111 x
= le16_to_cpup((__le16
*)&data
[4]);
112 y
= le16_to_cpup((__le16
*)&data
[6]);
113 pressure
= le16_to_cpup((__le16
*)&data
[8]);
115 input_report_key(input
, BTN_TOUCH
, tsw
|| ers
);
116 input_report_key(input
, BTN_TOOL_PEN
, tsw
);
117 input_report_key(input
, BTN_TOOL_RUBBER
, ers
);
118 input_report_key(input
, BTN_STYLUS
, f1
);
119 input_report_key(input
, BTN_STYLUS2
, f2
);
120 input_report_abs(input
, ABS_X
, x
);
121 input_report_abs(input
, ABS_Y
, y
);
122 input_report_abs(input
, ABS_PRESSURE
, pressure
);
129 static int wacom_i2c_open(struct input_dev
*dev
)
131 struct wacom_i2c
*wac_i2c
= input_get_drvdata(dev
);
132 struct i2c_client
*client
= wac_i2c
->client
;
134 enable_irq(client
->irq
);
139 static void wacom_i2c_close(struct input_dev
*dev
)
141 struct wacom_i2c
*wac_i2c
= input_get_drvdata(dev
);
142 struct i2c_client
*client
= wac_i2c
->client
;
144 disable_irq(client
->irq
);
147 static int wacom_i2c_probe(struct i2c_client
*client
,
148 const struct i2c_device_id
*id
)
150 struct wacom_i2c
*wac_i2c
;
151 struct input_dev
*input
;
152 struct wacom_features features
= { 0 };
155 if (!i2c_check_functionality(client
->adapter
, I2C_FUNC_I2C
)) {
156 dev_err(&client
->dev
, "i2c_check_functionality error\n");
160 error
= wacom_query_device(client
, &features
);
164 wac_i2c
= kzalloc(sizeof(*wac_i2c
), GFP_KERNEL
);
165 input
= input_allocate_device();
166 if (!wac_i2c
|| !input
) {
171 wac_i2c
->client
= client
;
172 wac_i2c
->input
= input
;
174 input
->name
= "Wacom I2C Digitizer";
175 input
->id
.bustype
= BUS_I2C
;
176 input
->id
.vendor
= 0x56a;
177 input
->id
.version
= features
.fw_version
;
178 input
->dev
.parent
= &client
->dev
;
179 input
->open
= wacom_i2c_open
;
180 input
->close
= wacom_i2c_close
;
182 input
->evbit
[0] |= BIT_MASK(EV_KEY
) | BIT_MASK(EV_ABS
);
184 __set_bit(BTN_TOOL_PEN
, input
->keybit
);
185 __set_bit(BTN_TOOL_RUBBER
, input
->keybit
);
186 __set_bit(BTN_STYLUS
, input
->keybit
);
187 __set_bit(BTN_STYLUS2
, input
->keybit
);
188 __set_bit(BTN_TOUCH
, input
->keybit
);
190 input_set_abs_params(input
, ABS_X
, 0, features
.x_max
, 0, 0);
191 input_set_abs_params(input
, ABS_Y
, 0, features
.y_max
, 0, 0);
192 input_set_abs_params(input
, ABS_PRESSURE
,
193 0, features
.pressure_max
, 0, 0);
195 input_set_drvdata(input
, wac_i2c
);
197 error
= request_threaded_irq(client
->irq
, NULL
, wacom_i2c_irq
,
198 IRQF_TRIGGER_LOW
| IRQF_ONESHOT
,
199 "wacom_i2c", wac_i2c
);
201 dev_err(&client
->dev
,
202 "Failed to enable IRQ, error: %d\n", error
);
206 /* Disable the IRQ, we'll enable it in wac_i2c_open() */
207 disable_irq(client
->irq
);
209 error
= input_register_device(wac_i2c
->input
);
211 dev_err(&client
->dev
,
212 "Failed to register input device, error: %d\n", error
);
216 i2c_set_clientdata(client
, wac_i2c
);
220 free_irq(client
->irq
, wac_i2c
);
222 input_free_device(input
);
228 static int wacom_i2c_remove(struct i2c_client
*client
)
230 struct wacom_i2c
*wac_i2c
= i2c_get_clientdata(client
);
232 free_irq(client
->irq
, wac_i2c
);
233 input_unregister_device(wac_i2c
->input
);
239 #ifdef CONFIG_PM_SLEEP
240 static int wacom_i2c_suspend(struct device
*dev
)
242 struct i2c_client
*client
= to_i2c_client(dev
);
244 disable_irq(client
->irq
);
249 static int wacom_i2c_resume(struct device
*dev
)
251 struct i2c_client
*client
= to_i2c_client(dev
);
253 enable_irq(client
->irq
);
259 static SIMPLE_DEV_PM_OPS(wacom_i2c_pm
, wacom_i2c_suspend
, wacom_i2c_resume
);
261 static const struct i2c_device_id wacom_i2c_id
[] = {
262 { "WAC_I2C_EMR", 0 },
265 MODULE_DEVICE_TABLE(i2c
, wacom_i2c_id
);
267 static struct i2c_driver wacom_i2c_driver
= {
270 .owner
= THIS_MODULE
,
274 .probe
= wacom_i2c_probe
,
275 .remove
= wacom_i2c_remove
,
276 .id_table
= wacom_i2c_id
,
278 module_i2c_driver(wacom_i2c_driver
);
280 MODULE_AUTHOR("Tatsunosuke Tobita <tobita.tatsunosuke@wacom.co.jp>");
281 MODULE_DESCRIPTION("WACOM EMR I2C Driver");
282 MODULE_LICENSE("GPL");