1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Wacom Penabled Driver for I2C |
4 | * |
5 | * Copyright (c) 2011 - 2013 Tatsunosuke Tobita, Wacom. |
6 | * <tobita.tatsunosuke@wacom.co.jp> |
7 | */ |
8 | |
9 | #include <linux/bits.h> |
10 | #include <linux/module.h> |
11 | #include <linux/input.h> |
12 | #include <linux/i2c.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/irq.h> |
15 | #include <linux/interrupt.h> |
16 | #include <asm/unaligned.h> |
17 | |
18 | /* Bitmasks (for data[3]) */ |
19 | #define WACOM_TIP_SWITCH BIT(0) |
20 | #define WACOM_BARREL_SWITCH BIT(1) |
21 | #define WACOM_ERASER BIT(2) |
22 | #define WACOM_INVERT BIT(3) |
23 | #define WACOM_BARREL_SWITCH_2 BIT(4) |
24 | #define WACOM_IN_PROXIMITY BIT(5) |
25 | |
26 | /* Registers */ |
27 | #define WACOM_COMMAND_LSB 0x04 |
28 | #define WACOM_COMMAND_MSB 0x00 |
29 | |
30 | #define WACOM_DATA_LSB 0x05 |
31 | #define WACOM_DATA_MSB 0x00 |
32 | |
33 | /* Report types */ |
34 | #define REPORT_FEATURE 0x30 |
35 | |
36 | /* Requests / operations */ |
37 | #define OPCODE_GET_REPORT 0x02 |
38 | |
39 | #define WACOM_QUERY_REPORT 3 |
40 | #define WACOM_QUERY_SIZE 19 |
41 | |
42 | struct wacom_features { |
43 | int x_max; |
44 | int y_max; |
45 | int pressure_max; |
46 | char fw_version; |
47 | }; |
48 | |
49 | struct wacom_i2c { |
50 | struct i2c_client *client; |
51 | struct input_dev *input; |
52 | u8 data[WACOM_QUERY_SIZE]; |
53 | bool prox; |
54 | int tool; |
55 | }; |
56 | |
57 | static int wacom_query_device(struct i2c_client *client, |
58 | struct wacom_features *features) |
59 | { |
60 | u8 get_query_data_cmd[] = { |
61 | WACOM_COMMAND_LSB, |
62 | WACOM_COMMAND_MSB, |
63 | REPORT_FEATURE | WACOM_QUERY_REPORT, |
64 | OPCODE_GET_REPORT, |
65 | WACOM_DATA_LSB, |
66 | WACOM_DATA_MSB, |
67 | }; |
68 | u8 data[WACOM_QUERY_SIZE]; |
69 | int ret; |
70 | |
71 | struct i2c_msg msgs[] = { |
72 | /* Request reading of feature ReportID: 3 (Pen Query Data) */ |
73 | { |
74 | .addr = client->addr, |
75 | .flags = 0, |
76 | .len = sizeof(get_query_data_cmd), |
77 | .buf = get_query_data_cmd, |
78 | }, |
79 | { |
80 | .addr = client->addr, |
81 | .flags = I2C_M_RD, |
82 | .len = sizeof(data), |
83 | .buf = data, |
84 | }, |
85 | }; |
86 | |
87 | ret = i2c_transfer(adap: client->adapter, msgs, ARRAY_SIZE(msgs)); |
88 | if (ret < 0) |
89 | return ret; |
90 | if (ret != ARRAY_SIZE(msgs)) |
91 | return -EIO; |
92 | |
93 | features->x_max = get_unaligned_le16(p: &data[3]); |
94 | features->y_max = get_unaligned_le16(p: &data[5]); |
95 | features->pressure_max = get_unaligned_le16(p: &data[11]); |
96 | features->fw_version = get_unaligned_le16(p: &data[13]); |
97 | |
98 | dev_dbg(&client->dev, |
99 | "x_max:%d, y_max:%d, pressure:%d, fw:%d\n" , |
100 | features->x_max, features->y_max, |
101 | features->pressure_max, features->fw_version); |
102 | |
103 | return 0; |
104 | } |
105 | |
106 | static irqreturn_t wacom_i2c_irq(int irq, void *dev_id) |
107 | { |
108 | struct wacom_i2c *wac_i2c = dev_id; |
109 | struct input_dev *input = wac_i2c->input; |
110 | u8 *data = wac_i2c->data; |
111 | unsigned int x, y, pressure; |
112 | unsigned char tsw, f1, f2, ers; |
113 | int error; |
114 | |
115 | error = i2c_master_recv(client: wac_i2c->client, |
116 | buf: wac_i2c->data, count: sizeof(wac_i2c->data)); |
117 | if (error < 0) |
118 | goto out; |
119 | |
120 | tsw = data[3] & WACOM_TIP_SWITCH; |
121 | ers = data[3] & WACOM_ERASER; |
122 | f1 = data[3] & WACOM_BARREL_SWITCH; |
123 | f2 = data[3] & WACOM_BARREL_SWITCH_2; |
124 | x = le16_to_cpup(p: (__le16 *)&data[4]); |
125 | y = le16_to_cpup(p: (__le16 *)&data[6]); |
126 | pressure = le16_to_cpup(p: (__le16 *)&data[8]); |
127 | |
128 | if (!wac_i2c->prox) |
129 | wac_i2c->tool = (data[3] & (WACOM_ERASER | WACOM_INVERT)) ? |
130 | BTN_TOOL_RUBBER : BTN_TOOL_PEN; |
131 | |
132 | wac_i2c->prox = data[3] & WACOM_IN_PROXIMITY; |
133 | |
134 | input_report_key(dev: input, BTN_TOUCH, value: tsw || ers); |
135 | input_report_key(dev: input, code: wac_i2c->tool, value: wac_i2c->prox); |
136 | input_report_key(dev: input, BTN_STYLUS, value: f1); |
137 | input_report_key(dev: input, BTN_STYLUS2, value: f2); |
138 | input_report_abs(dev: input, ABS_X, value: x); |
139 | input_report_abs(dev: input, ABS_Y, value: y); |
140 | input_report_abs(dev: input, ABS_PRESSURE, value: pressure); |
141 | input_sync(dev: input); |
142 | |
143 | out: |
144 | return IRQ_HANDLED; |
145 | } |
146 | |
147 | static int wacom_i2c_open(struct input_dev *dev) |
148 | { |
149 | struct wacom_i2c *wac_i2c = input_get_drvdata(dev); |
150 | struct i2c_client *client = wac_i2c->client; |
151 | |
152 | enable_irq(irq: client->irq); |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | static void wacom_i2c_close(struct input_dev *dev) |
158 | { |
159 | struct wacom_i2c *wac_i2c = input_get_drvdata(dev); |
160 | struct i2c_client *client = wac_i2c->client; |
161 | |
162 | disable_irq(irq: client->irq); |
163 | } |
164 | |
165 | static int wacom_i2c_probe(struct i2c_client *client) |
166 | { |
167 | struct device *dev = &client->dev; |
168 | struct wacom_i2c *wac_i2c; |
169 | struct input_dev *input; |
170 | struct wacom_features features = { 0 }; |
171 | int error; |
172 | |
173 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) { |
174 | dev_err(dev, "i2c_check_functionality error\n" ); |
175 | return -EIO; |
176 | } |
177 | |
178 | error = wacom_query_device(client, features: &features); |
179 | if (error) |
180 | return error; |
181 | |
182 | wac_i2c = devm_kzalloc(dev, size: sizeof(*wac_i2c), GFP_KERNEL); |
183 | if (!wac_i2c) |
184 | return -ENOMEM; |
185 | |
186 | wac_i2c->client = client; |
187 | |
188 | input = devm_input_allocate_device(dev); |
189 | if (!input) |
190 | return -ENOMEM; |
191 | |
192 | wac_i2c->input = input; |
193 | |
194 | input->name = "Wacom I2C Digitizer" ; |
195 | input->id.bustype = BUS_I2C; |
196 | input->id.vendor = 0x56a; |
197 | input->id.version = features.fw_version; |
198 | input->open = wacom_i2c_open; |
199 | input->close = wacom_i2c_close; |
200 | |
201 | input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
202 | |
203 | __set_bit(BTN_TOOL_PEN, input->keybit); |
204 | __set_bit(BTN_TOOL_RUBBER, input->keybit); |
205 | __set_bit(BTN_STYLUS, input->keybit); |
206 | __set_bit(BTN_STYLUS2, input->keybit); |
207 | __set_bit(BTN_TOUCH, input->keybit); |
208 | |
209 | input_set_abs_params(dev: input, ABS_X, min: 0, max: features.x_max, fuzz: 0, flat: 0); |
210 | input_set_abs_params(dev: input, ABS_Y, min: 0, max: features.y_max, fuzz: 0, flat: 0); |
211 | input_set_abs_params(dev: input, ABS_PRESSURE, |
212 | min: 0, max: features.pressure_max, fuzz: 0, flat: 0); |
213 | |
214 | input_set_drvdata(dev: input, data: wac_i2c); |
215 | |
216 | error = devm_request_threaded_irq(dev, irq: client->irq, NULL, thread_fn: wacom_i2c_irq, |
217 | IRQF_ONESHOT, devname: "wacom_i2c" , dev_id: wac_i2c); |
218 | if (error) { |
219 | dev_err(dev, "Failed to request IRQ: %d\n" , error); |
220 | return error; |
221 | } |
222 | |
223 | /* Disable the IRQ, we'll enable it in wac_i2c_open() */ |
224 | disable_irq(irq: client->irq); |
225 | |
226 | error = input_register_device(wac_i2c->input); |
227 | if (error) { |
228 | dev_err(dev, "Failed to register input device: %d\n" , error); |
229 | return error; |
230 | } |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | static int wacom_i2c_suspend(struct device *dev) |
236 | { |
237 | struct i2c_client *client = to_i2c_client(dev); |
238 | |
239 | disable_irq(irq: client->irq); |
240 | |
241 | return 0; |
242 | } |
243 | |
244 | static int wacom_i2c_resume(struct device *dev) |
245 | { |
246 | struct i2c_client *client = to_i2c_client(dev); |
247 | |
248 | enable_irq(irq: client->irq); |
249 | |
250 | return 0; |
251 | } |
252 | |
253 | static DEFINE_SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume); |
254 | |
255 | static const struct i2c_device_id wacom_i2c_id[] = { |
256 | { "WAC_I2C_EMR" , 0 }, |
257 | { }, |
258 | }; |
259 | MODULE_DEVICE_TABLE(i2c, wacom_i2c_id); |
260 | |
261 | static struct i2c_driver wacom_i2c_driver = { |
262 | .driver = { |
263 | .name = "wacom_i2c" , |
264 | .pm = pm_sleep_ptr(&wacom_i2c_pm), |
265 | }, |
266 | |
267 | .probe = wacom_i2c_probe, |
268 | .id_table = wacom_i2c_id, |
269 | }; |
270 | module_i2c_driver(wacom_i2c_driver); |
271 | |
272 | MODULE_AUTHOR("Tatsunosuke Tobita <tobita.tatsunosuke@wacom.co.jp>" ); |
273 | MODULE_DESCRIPTION("WACOM EMR I2C Driver" ); |
274 | MODULE_LICENSE("GPL" ); |
275 | |