]>
Commit | Line | Data |
---|---|---|
82c29810 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
60347c19 SK |
2 | /* |
3 | * Driver for cypress touch screen controller | |
4 | * | |
5 | * Copyright (c) 2009 Aava Mobile | |
6 | * | |
7 | * Some cleanups by Alan Cox <alan@linux.intel.com> | |
60347c19 SK |
8 | */ |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/input.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/i2c.h> | |
17 | #include <linux/gpio.h> | |
18 | #include <linux/input/cy8ctmg110_pdata.h> | |
19 | ||
20 | #define CY8CTMG110_DRIVER_NAME "cy8ctmg110" | |
21 | ||
22 | /* Touch coordinates */ | |
23 | #define CY8CTMG110_X_MIN 0 | |
24 | #define CY8CTMG110_Y_MIN 0 | |
25 | #define CY8CTMG110_X_MAX 759 | |
26 | #define CY8CTMG110_Y_MAX 465 | |
27 | ||
28 | ||
29 | /* cy8ctmg110 register definitions */ | |
30 | #define CY8CTMG110_TOUCH_WAKEUP_TIME 0 | |
31 | #define CY8CTMG110_TOUCH_SLEEP_TIME 2 | |
32 | #define CY8CTMG110_TOUCH_X1 3 | |
33 | #define CY8CTMG110_TOUCH_Y1 5 | |
34 | #define CY8CTMG110_TOUCH_X2 7 | |
35 | #define CY8CTMG110_TOUCH_Y2 9 | |
36 | #define CY8CTMG110_FINGERS 11 | |
37 | #define CY8CTMG110_GESTURE 12 | |
38 | #define CY8CTMG110_REG_MAX 13 | |
39 | ||
40 | ||
41 | /* | |
42 | * The touch driver structure. | |
43 | */ | |
44 | struct cy8ctmg110 { | |
45 | struct input_dev *input; | |
46 | char phys[32]; | |
47 | struct i2c_client *client; | |
48 | int reset_pin; | |
49 | int irq_pin; | |
50 | }; | |
51 | ||
52 | /* | |
53 | * cy8ctmg110_power is the routine that is called when touch hardware | |
54 | * will powered off or on. | |
55 | */ | |
56 | static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron) | |
57 | { | |
58 | if (ts->reset_pin) | |
59 | gpio_direction_output(ts->reset_pin, 1 - poweron); | |
60 | } | |
61 | ||
62 | static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg, | |
63 | unsigned char len, unsigned char *value) | |
64 | { | |
65 | struct i2c_client *client = tsc->client; | |
f1b50760 | 66 | int ret; |
60347c19 SK |
67 | unsigned char i2c_data[6]; |
68 | ||
69 | BUG_ON(len > 5); | |
70 | ||
71 | i2c_data[0] = reg; | |
72 | memcpy(i2c_data + 1, value, len); | |
73 | ||
74 | ret = i2c_master_send(client, i2c_data, len + 1); | |
21184c4e | 75 | if (ret != len + 1) { |
60347c19 | 76 | dev_err(&client->dev, "i2c write data cmd failed\n"); |
21184c4e | 77 | return ret < 0 ? ret : -EIO; |
60347c19 SK |
78 | } |
79 | ||
80 | return 0; | |
81 | } | |
82 | ||
83 | static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc, | |
84 | unsigned char *data, unsigned char len, unsigned char cmd) | |
85 | { | |
86 | struct i2c_client *client = tsc->client; | |
f1b50760 | 87 | int ret; |
60347c19 SK |
88 | struct i2c_msg msg[2] = { |
89 | /* first write slave position to i2c devices */ | |
9493d974 SD |
90 | { |
91 | .addr = client->addr, | |
92 | .len = 1, | |
93 | .buf = &cmd | |
94 | }, | |
60347c19 | 95 | /* Second read data from position */ |
9493d974 SD |
96 | { |
97 | .addr = client->addr, | |
98 | .flags = I2C_M_RD, | |
99 | .len = len, | |
100 | .buf = data | |
101 | } | |
60347c19 SK |
102 | }; |
103 | ||
104 | ret = i2c_transfer(client->adapter, msg, 2); | |
105 | if (ret < 0) | |
106 | return ret; | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc) | |
112 | { | |
113 | struct input_dev *input = tsc->input; | |
114 | unsigned char reg_p[CY8CTMG110_REG_MAX]; | |
115 | int x, y; | |
116 | ||
117 | memset(reg_p, 0, CY8CTMG110_REG_MAX); | |
118 | ||
119 | /* Reading coordinates */ | |
120 | if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0) | |
121 | return -EIO; | |
122 | ||
123 | y = reg_p[2] << 8 | reg_p[3]; | |
124 | x = reg_p[0] << 8 | reg_p[1]; | |
125 | ||
126 | /* Number of touch */ | |
127 | if (reg_p[8] == 0) { | |
128 | input_report_key(input, BTN_TOUCH, 0); | |
129 | } else { | |
130 | input_report_key(input, BTN_TOUCH, 1); | |
131 | input_report_abs(input, ABS_X, x); | |
132 | input_report_abs(input, ABS_Y, y); | |
133 | } | |
134 | ||
135 | input_sync(input); | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep) | |
141 | { | |
142 | unsigned char reg_p[3]; | |
143 | ||
144 | if (sleep) { | |
145 | reg_p[0] = 0x00; | |
146 | reg_p[1] = 0xff; | |
147 | reg_p[2] = 5; | |
148 | } else { | |
149 | reg_p[0] = 0x10; | |
150 | reg_p[1] = 0xff; | |
151 | reg_p[2] = 0; | |
152 | } | |
153 | ||
154 | return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p); | |
155 | } | |
156 | ||
157 | static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id) | |
158 | { | |
159 | struct cy8ctmg110 *tsc = dev_id; | |
160 | ||
161 | cy8ctmg110_touch_pos(tsc); | |
162 | ||
163 | return IRQ_HANDLED; | |
164 | } | |
165 | ||
5298cc4c | 166 | static int cy8ctmg110_probe(struct i2c_client *client, |
60347c19 SK |
167 | const struct i2c_device_id *id) |
168 | { | |
c838cb3d | 169 | const struct cy8ctmg110_pdata *pdata = dev_get_platdata(&client->dev); |
60347c19 SK |
170 | struct cy8ctmg110 *ts; |
171 | struct input_dev *input_dev; | |
172 | int err; | |
173 | ||
174 | /* No pdata no way forward */ | |
175 | if (pdata == NULL) { | |
176 | dev_err(&client->dev, "no pdata\n"); | |
177 | return -ENODEV; | |
178 | } | |
179 | ||
180 | if (!i2c_check_functionality(client->adapter, | |
181 | I2C_FUNC_SMBUS_READ_WORD_DATA)) | |
182 | return -EIO; | |
183 | ||
184 | ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL); | |
185 | input_dev = input_allocate_device(); | |
186 | if (!ts || !input_dev) { | |
187 | err = -ENOMEM; | |
188 | goto err_free_mem; | |
189 | } | |
190 | ||
191 | ts->client = client; | |
192 | ts->input = input_dev; | |
2c204109 AL |
193 | ts->reset_pin = pdata->reset_pin; |
194 | ts->irq_pin = pdata->irq_pin; | |
60347c19 SK |
195 | |
196 | snprintf(ts->phys, sizeof(ts->phys), | |
197 | "%s/input0", dev_name(&client->dev)); | |
198 | ||
199 | input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen"; | |
200 | input_dev->phys = ts->phys; | |
201 | input_dev->id.bustype = BUS_I2C; | |
202 | input_dev->dev.parent = &client->dev; | |
203 | ||
204 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | |
205 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | |
206 | ||
207 | input_set_abs_params(input_dev, ABS_X, | |
a4e6aad6 | 208 | CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0); |
60347c19 | 209 | input_set_abs_params(input_dev, ABS_Y, |
a4e6aad6 | 210 | CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0); |
60347c19 SK |
211 | |
212 | if (ts->reset_pin) { | |
213 | err = gpio_request(ts->reset_pin, NULL); | |
214 | if (err) { | |
215 | dev_err(&client->dev, | |
216 | "Unable to request GPIO pin %d.\n", | |
217 | ts->reset_pin); | |
218 | goto err_free_mem; | |
219 | } | |
220 | } | |
221 | ||
222 | cy8ctmg110_power(ts, true); | |
223 | cy8ctmg110_set_sleepmode(ts, false); | |
224 | ||
225 | err = gpio_request(ts->irq_pin, "touch_irq_key"); | |
226 | if (err < 0) { | |
227 | dev_err(&client->dev, | |
228 | "Failed to request GPIO %d, error %d\n", | |
229 | ts->irq_pin, err); | |
230 | goto err_shutoff_device; | |
231 | } | |
232 | ||
233 | err = gpio_direction_input(ts->irq_pin); | |
234 | if (err < 0) { | |
235 | dev_err(&client->dev, | |
236 | "Failed to configure input direction for GPIO %d, error %d\n", | |
237 | ts->irq_pin, err); | |
238 | goto err_free_irq_gpio; | |
239 | } | |
240 | ||
241 | client->irq = gpio_to_irq(ts->irq_pin); | |
242 | if (client->irq < 0) { | |
243 | err = client->irq; | |
244 | dev_err(&client->dev, | |
245 | "Unable to get irq number for GPIO %d, error %d\n", | |
246 | ts->irq_pin, err); | |
247 | goto err_free_irq_gpio; | |
248 | } | |
249 | ||
250 | err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread, | |
9b7e31bb LPC |
251 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
252 | "touch_reset_key", ts); | |
60347c19 SK |
253 | if (err < 0) { |
254 | dev_err(&client->dev, | |
255 | "irq %d busy? error %d\n", client->irq, err); | |
256 | goto err_free_irq_gpio; | |
257 | } | |
258 | ||
259 | err = input_register_device(input_dev); | |
260 | if (err) | |
261 | goto err_free_irq; | |
262 | ||
263 | i2c_set_clientdata(client, ts); | |
264 | device_init_wakeup(&client->dev, 1); | |
265 | return 0; | |
266 | ||
267 | err_free_irq: | |
268 | free_irq(client->irq, ts); | |
269 | err_free_irq_gpio: | |
270 | gpio_free(ts->irq_pin); | |
271 | err_shutoff_device: | |
272 | cy8ctmg110_set_sleepmode(ts, true); | |
273 | cy8ctmg110_power(ts, false); | |
274 | if (ts->reset_pin) | |
275 | gpio_free(ts->reset_pin); | |
276 | err_free_mem: | |
277 | input_free_device(input_dev); | |
278 | kfree(ts); | |
279 | return err; | |
280 | } | |
281 | ||
02b6a58b | 282 | static int __maybe_unused cy8ctmg110_suspend(struct device *dev) |
60347c19 | 283 | { |
93f38e91 | 284 | struct i2c_client *client = to_i2c_client(dev); |
60347c19 SK |
285 | struct cy8ctmg110 *ts = i2c_get_clientdata(client); |
286 | ||
287 | if (device_may_wakeup(&client->dev)) | |
288 | enable_irq_wake(client->irq); | |
289 | else { | |
290 | cy8ctmg110_set_sleepmode(ts, true); | |
291 | cy8ctmg110_power(ts, false); | |
292 | } | |
293 | return 0; | |
294 | } | |
295 | ||
02b6a58b | 296 | static int __maybe_unused cy8ctmg110_resume(struct device *dev) |
60347c19 | 297 | { |
93f38e91 | 298 | struct i2c_client *client = to_i2c_client(dev); |
60347c19 SK |
299 | struct cy8ctmg110 *ts = i2c_get_clientdata(client); |
300 | ||
301 | if (device_may_wakeup(&client->dev)) | |
302 | disable_irq_wake(client->irq); | |
303 | else { | |
304 | cy8ctmg110_power(ts, true); | |
305 | cy8ctmg110_set_sleepmode(ts, false); | |
306 | } | |
307 | return 0; | |
308 | } | |
93f38e91 MB |
309 | |
310 | static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume); | |
60347c19 | 311 | |
e2619cf7 | 312 | static int cy8ctmg110_remove(struct i2c_client *client) |
60347c19 SK |
313 | { |
314 | struct cy8ctmg110 *ts = i2c_get_clientdata(client); | |
315 | ||
316 | cy8ctmg110_set_sleepmode(ts, true); | |
317 | cy8ctmg110_power(ts, false); | |
318 | ||
319 | free_irq(client->irq, ts); | |
320 | input_unregister_device(ts->input); | |
321 | gpio_free(ts->irq_pin); | |
322 | if (ts->reset_pin) | |
323 | gpio_free(ts->reset_pin); | |
324 | kfree(ts); | |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
d448303a | 329 | static const struct i2c_device_id cy8ctmg110_idtable[] = { |
60347c19 SK |
330 | { CY8CTMG110_DRIVER_NAME, 1 }, |
331 | { } | |
332 | }; | |
333 | ||
334 | MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); | |
335 | ||
336 | static struct i2c_driver cy8ctmg110_driver = { | |
337 | .driver = { | |
60347c19 | 338 | .name = CY8CTMG110_DRIVER_NAME, |
93f38e91 | 339 | .pm = &cy8ctmg110_pm, |
60347c19 SK |
340 | }, |
341 | .id_table = cy8ctmg110_idtable, | |
342 | .probe = cy8ctmg110_probe, | |
1cb0aa88 | 343 | .remove = cy8ctmg110_remove, |
60347c19 SK |
344 | }; |
345 | ||
1b92c1cf | 346 | module_i2c_driver(cy8ctmg110_driver); |
60347c19 SK |
347 | |
348 | MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>"); | |
349 | MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); | |
350 | MODULE_LICENSE("GPL v2"); |