]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/input/touchscreen/ili210x.c
Input: ili210x - convert to devm_ functions
[mirror_ubuntu-hirsute-kernel.git] / drivers / input / touchscreen / ili210x.c
CommitLineData
5c6a7a62
OS
1#include <linux/module.h>
2#include <linux/i2c.h>
3#include <linux/interrupt.h>
4#include <linux/slab.h>
5#include <linux/input.h>
6#include <linux/input/mt.h>
7#include <linux/delay.h>
8#include <linux/workqueue.h>
5c6a7a62
OS
9
10#define MAX_TOUCHES 2
11#define DEFAULT_POLL_PERIOD 20
12
13/* Touchscreen commands */
14#define REG_TOUCHDATA 0x10
15#define REG_PANEL_INFO 0x20
16#define REG_FIRMWARE_VERSION 0x40
17#define REG_CALIBRATE 0xcc
18
19struct finger {
20 u8 x_low;
21 u8 x_high;
22 u8 y_low;
23 u8 y_high;
24} __packed;
25
26struct touchdata {
27 u8 status;
28 struct finger finger[MAX_TOUCHES];
29} __packed;
30
31struct panel_info {
32 struct finger finger_max;
33 u8 xchannel_num;
34 u8 ychannel_num;
35} __packed;
36
37struct firmware_version {
38 u8 id;
39 u8 major;
40 u8 minor;
41} __packed;
42
43struct ili210x {
44 struct i2c_client *client;
45 struct input_dev *input;
5c6a7a62
OS
46 unsigned int poll_period;
47 struct delayed_work dwork;
48};
49
50static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
51 size_t len)
52{
53 struct i2c_msg msg[2] = {
54 {
55 .addr = client->addr,
56 .flags = 0,
57 .len = 1,
58 .buf = &reg,
59 },
60 {
61 .addr = client->addr,
62 .flags = I2C_M_RD,
63 .len = len,
64 .buf = buf,
65 }
66 };
67
68 if (i2c_transfer(client->adapter, msg, 2) != 2) {
69 dev_err(&client->dev, "i2c transfer failed\n");
70 return -EIO;
71 }
72
73 return 0;
74}
75
76static void ili210x_report_events(struct input_dev *input,
77 const struct touchdata *touchdata)
78{
79 int i;
80 bool touch;
81 unsigned int x, y;
82 const struct finger *finger;
83
84 for (i = 0; i < MAX_TOUCHES; i++) {
85 input_mt_slot(input, i);
86
87 finger = &touchdata->finger[i];
88
89 touch = touchdata->status & (1 << i);
90 input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
91 if (touch) {
92 x = finger->x_low | (finger->x_high << 8);
93 y = finger->y_low | (finger->y_high << 8);
94
95 input_report_abs(input, ABS_MT_POSITION_X, x);
96 input_report_abs(input, ABS_MT_POSITION_Y, y);
97 }
98 }
99
100 input_mt_report_pointer_emulation(input, false);
101 input_sync(input);
102}
103
5c6a7a62
OS
104static void ili210x_work(struct work_struct *work)
105{
106 struct ili210x *priv = container_of(work, struct ili210x,
107 dwork.work);
108 struct i2c_client *client = priv->client;
109 struct touchdata touchdata;
110 int error;
111
112 error = ili210x_read_reg(client, REG_TOUCHDATA,
113 &touchdata, sizeof(touchdata));
114 if (error) {
115 dev_err(&client->dev,
116 "Unable to get touchdata, err = %d\n", error);
117 return;
118 }
119
120 ili210x_report_events(priv->input, &touchdata);
121
2fa92839 122 if (touchdata.status & 0xf3)
5c6a7a62
OS
123 schedule_delayed_work(&priv->dwork,
124 msecs_to_jiffies(priv->poll_period));
125}
126
127static irqreturn_t ili210x_irq(int irq, void *irq_data)
128{
129 struct ili210x *priv = irq_data;
130
131 schedule_delayed_work(&priv->dwork, 0);
132
133 return IRQ_HANDLED;
134}
135
136static ssize_t ili210x_calibrate(struct device *dev,
137 struct device_attribute *attr,
138 const char *buf, size_t count)
139{
140 struct i2c_client *client = to_i2c_client(dev);
141 struct ili210x *priv = i2c_get_clientdata(client);
142 unsigned long calibrate;
143 int rc;
144 u8 cmd = REG_CALIBRATE;
145
146 if (kstrtoul(buf, 10, &calibrate))
147 return -EINVAL;
148
149 if (calibrate > 1)
150 return -EINVAL;
151
152 if (calibrate) {
153 rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
154 if (rc != sizeof(cmd))
155 return -EIO;
156 }
157
158 return count;
159}
b27c0d0c 160static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate);
5c6a7a62
OS
161
162static struct attribute *ili210x_attributes[] = {
163 &dev_attr_calibrate.attr,
164 NULL,
165};
166
167static const struct attribute_group ili210x_attr_group = {
168 .attrs = ili210x_attributes,
169};
170
5298cc4c 171static int ili210x_i2c_probe(struct i2c_client *client,
5c6a7a62
OS
172 const struct i2c_device_id *id)
173{
174 struct device *dev = &client->dev;
5c6a7a62
OS
175 struct ili210x *priv;
176 struct input_dev *input;
177 struct panel_info panel;
178 struct firmware_version firmware;
179 int xmax, ymax;
180 int error;
181
182 dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
183
5c6a7a62
OS
184 if (client->irq <= 0) {
185 dev_err(dev, "No IRQ!\n");
186 return -EINVAL;
187 }
188
189 /* Get firmware version */
190 error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
191 &firmware, sizeof(firmware));
192 if (error) {
193 dev_err(dev, "Failed to get firmware version, err: %d\n",
194 error);
195 return error;
196 }
197
198 /* get panel info */
199 error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
200 if (error) {
971bd8fa 201 dev_err(dev, "Failed to get panel information, err: %d\n",
5c6a7a62
OS
202 error);
203 return error;
204 }
205
206 xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
207 ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
208
63083fd5
MV
209 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
210 if (!priv)
211 return -ENOMEM;
212
213 input = devm_input_allocate_device(dev);
214 if (!input)
215 return -ENOMEM;
5c6a7a62
OS
216
217 priv->client = client;
218 priv->input = input;
626feb86 219 priv->poll_period = DEFAULT_POLL_PERIOD;
5c6a7a62
OS
220 INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
221
222 /* Setup input device */
223 input->name = "ILI210x Touchscreen";
224 input->id.bustype = BUS_I2C;
225 input->dev.parent = dev;
226
227 __set_bit(EV_SYN, input->evbit);
228 __set_bit(EV_KEY, input->evbit);
229 __set_bit(EV_ABS, input->evbit);
230 __set_bit(BTN_TOUCH, input->keybit);
231
232 /* Single touch */
233 input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
234 input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
235
236 /* Multi touch */
b4adbbef 237 input_mt_init_slots(input, MAX_TOUCHES, 0);
5c6a7a62
OS
238 input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
239 input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
240
5c6a7a62
OS
241 i2c_set_clientdata(client, priv);
242
626feb86 243 error = request_irq(client->irq, ili210x_irq, 0,
5c6a7a62
OS
244 client->name, priv);
245 if (error) {
246 dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
247 error);
248 goto err_free_mem;
249 }
250
251 error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
252 if (error) {
253 dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
254 error);
255 goto err_free_irq;
256 }
257
258 error = input_register_device(priv->input);
259 if (error) {
971bd8fa 260 dev_err(dev, "Cannot register input device, err: %d\n", error);
5c6a7a62
OS
261 goto err_remove_sysfs;
262 }
263
d7ddf154 264 device_init_wakeup(dev, 1);
5c6a7a62
OS
265
266 dev_dbg(dev,
267 "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
268 client->irq, firmware.id, firmware.major, firmware.minor);
269
270 return 0;
271
272err_remove_sysfs:
273 sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
274err_free_irq:
275 free_irq(client->irq, priv);
276err_free_mem:
5c6a7a62
OS
277 return error;
278}
279
e2619cf7 280static int ili210x_i2c_remove(struct i2c_client *client)
5c6a7a62
OS
281{
282 struct ili210x *priv = i2c_get_clientdata(client);
283
284 sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
285 free_irq(priv->client->irq, priv);
286 cancel_delayed_work_sync(&priv->dwork);
5c6a7a62
OS
287
288 return 0;
289}
290
02b6a58b 291static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
5c6a7a62
OS
292{
293 struct i2c_client *client = to_i2c_client(dev);
294
295 if (device_may_wakeup(&client->dev))
296 enable_irq_wake(client->irq);
297
298 return 0;
299}
300
02b6a58b 301static int __maybe_unused ili210x_i2c_resume(struct device *dev)
5c6a7a62
OS
302{
303 struct i2c_client *client = to_i2c_client(dev);
304
305 if (device_may_wakeup(&client->dev))
306 disable_irq_wake(client->irq);
307
308 return 0;
309}
5c6a7a62
OS
310
311static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
312 ili210x_i2c_suspend, ili210x_i2c_resume);
313
314static const struct i2c_device_id ili210x_i2c_id[] = {
315 { "ili210x", 0 },
316 { }
317};
318MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
319
320static struct i2c_driver ili210x_ts_driver = {
321 .driver = {
322 .name = "ili210x_i2c",
5c6a7a62
OS
323 .pm = &ili210x_i2c_pm,
324 },
325 .id_table = ili210x_i2c_id,
326 .probe = ili210x_i2c_probe,
1cb0aa88 327 .remove = ili210x_i2c_remove,
5c6a7a62
OS
328};
329
330module_i2c_driver(ili210x_ts_driver);
331
332MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
333MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
334MODULE_LICENSE("GPL");