1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for ChipOne icn8318 i2c touchscreen controller |
4 | * |
5 | * Copyright (c) 2015 Red Hat Inc. |
6 | * |
7 | * Red Hat authors: |
8 | * Hans de Goede <hdegoede@redhat.com> |
9 | */ |
10 | |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/input.h> |
15 | #include <linux/input/mt.h> |
16 | #include <linux/input/touchscreen.h> |
17 | #include <linux/module.h> |
18 | #include <linux/of.h> |
19 | |
20 | #define ICN8318_REG_POWER 4 |
21 | #define ICN8318_REG_TOUCHDATA 16 |
22 | |
23 | #define ICN8318_POWER_ACTIVE 0 |
24 | #define ICN8318_POWER_MONITOR 1 |
25 | #define ICN8318_POWER_HIBERNATE 2 |
26 | |
27 | #define ICN8318_MAX_TOUCHES 5 |
28 | |
29 | struct icn8318_touch { |
30 | __u8 slot; |
31 | __be16 x; |
32 | __be16 y; |
33 | __u8 pressure; /* Seems more like finger width then pressure really */ |
34 | __u8 event; |
35 | /* The difference between 2 and 3 is unclear */ |
36 | #define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */ |
37 | #define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */ |
38 | #define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */ |
39 | #define ICN8318_EVENT_END 4 /* Finger lifted */ |
40 | } __packed; |
41 | |
42 | struct icn8318_touch_data { |
43 | __u8 softbutton; |
44 | __u8 touch_count; |
45 | struct icn8318_touch touches[ICN8318_MAX_TOUCHES]; |
46 | } __packed; |
47 | |
48 | struct icn8318_data { |
49 | struct i2c_client *client; |
50 | struct input_dev *input; |
51 | struct gpio_desc *wake_gpio; |
52 | struct touchscreen_properties prop; |
53 | }; |
54 | |
55 | static int icn8318_read_touch_data(struct i2c_client *client, |
56 | struct icn8318_touch_data *touch_data) |
57 | { |
58 | u8 reg = ICN8318_REG_TOUCHDATA; |
59 | struct i2c_msg msg[2] = { |
60 | { |
61 | .addr = client->addr, |
62 | .len = 1, |
63 | .buf = ® |
64 | }, |
65 | { |
66 | .addr = client->addr, |
67 | .flags = I2C_M_RD, |
68 | .len = sizeof(struct icn8318_touch_data), |
69 | .buf = (u8 *)touch_data |
70 | } |
71 | }; |
72 | |
73 | return i2c_transfer(adap: client->adapter, msgs: msg, num: 2); |
74 | } |
75 | |
76 | static inline bool icn8318_touch_active(u8 event) |
77 | { |
78 | return (event == ICN8318_EVENT_UPDATE1) || |
79 | (event == ICN8318_EVENT_UPDATE2); |
80 | } |
81 | |
82 | static irqreturn_t icn8318_irq(int irq, void *dev_id) |
83 | { |
84 | struct icn8318_data *data = dev_id; |
85 | struct device *dev = &data->client->dev; |
86 | struct icn8318_touch_data touch_data; |
87 | int i, ret; |
88 | |
89 | ret = icn8318_read_touch_data(client: data->client, touch_data: &touch_data); |
90 | if (ret < 0) { |
91 | dev_err(dev, "Error reading touch data: %d\n" , ret); |
92 | return IRQ_HANDLED; |
93 | } |
94 | |
95 | if (touch_data.softbutton) { |
96 | /* |
97 | * Other data is invalid when a softbutton is pressed. |
98 | * This needs some extra devicetree bindings to map the icn8318 |
99 | * softbutton codes to evdev codes. Currently no known devices |
100 | * use this. |
101 | */ |
102 | return IRQ_HANDLED; |
103 | } |
104 | |
105 | if (touch_data.touch_count > ICN8318_MAX_TOUCHES) { |
106 | dev_warn(dev, "Too much touches %d > %d\n" , |
107 | touch_data.touch_count, ICN8318_MAX_TOUCHES); |
108 | touch_data.touch_count = ICN8318_MAX_TOUCHES; |
109 | } |
110 | |
111 | for (i = 0; i < touch_data.touch_count; i++) { |
112 | struct icn8318_touch *touch = &touch_data.touches[i]; |
113 | bool act = icn8318_touch_active(event: touch->event); |
114 | |
115 | input_mt_slot(dev: data->input, slot: touch->slot); |
116 | input_mt_report_slot_state(dev: data->input, MT_TOOL_FINGER, active: act); |
117 | if (!act) |
118 | continue; |
119 | |
120 | touchscreen_report_pos(input: data->input, prop: &data->prop, |
121 | be16_to_cpu(touch->x), |
122 | be16_to_cpu(touch->y), multitouch: true); |
123 | } |
124 | |
125 | input_mt_sync_frame(dev: data->input); |
126 | input_sync(dev: data->input); |
127 | |
128 | return IRQ_HANDLED; |
129 | } |
130 | |
131 | static int icn8318_start(struct input_dev *dev) |
132 | { |
133 | struct icn8318_data *data = input_get_drvdata(dev); |
134 | |
135 | enable_irq(irq: data->client->irq); |
136 | gpiod_set_value_cansleep(desc: data->wake_gpio, value: 1); |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static void icn8318_stop(struct input_dev *dev) |
142 | { |
143 | struct icn8318_data *data = input_get_drvdata(dev); |
144 | |
145 | disable_irq(irq: data->client->irq); |
146 | i2c_smbus_write_byte_data(client: data->client, ICN8318_REG_POWER, |
147 | ICN8318_POWER_HIBERNATE); |
148 | gpiod_set_value_cansleep(desc: data->wake_gpio, value: 0); |
149 | } |
150 | |
151 | static int icn8318_suspend(struct device *dev) |
152 | { |
153 | struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); |
154 | |
155 | mutex_lock(&data->input->mutex); |
156 | if (input_device_enabled(dev: data->input)) |
157 | icn8318_stop(dev: data->input); |
158 | mutex_unlock(lock: &data->input->mutex); |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | static int icn8318_resume(struct device *dev) |
164 | { |
165 | struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); |
166 | |
167 | mutex_lock(&data->input->mutex); |
168 | if (input_device_enabled(dev: data->input)) |
169 | icn8318_start(dev: data->input); |
170 | mutex_unlock(lock: &data->input->mutex); |
171 | |
172 | return 0; |
173 | } |
174 | |
175 | static DEFINE_SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume); |
176 | |
177 | static int icn8318_probe(struct i2c_client *client) |
178 | { |
179 | struct device *dev = &client->dev; |
180 | struct icn8318_data *data; |
181 | struct input_dev *input; |
182 | int error; |
183 | |
184 | if (!client->irq) { |
185 | dev_err(dev, "Error no irq specified\n" ); |
186 | return -EINVAL; |
187 | } |
188 | |
189 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
190 | if (!data) |
191 | return -ENOMEM; |
192 | |
193 | data->wake_gpio = devm_gpiod_get(dev, con_id: "wake" , flags: GPIOD_OUT_LOW); |
194 | if (IS_ERR(ptr: data->wake_gpio)) |
195 | return dev_err_probe(dev, err: PTR_ERR(ptr: data->wake_gpio), fmt: "Error getting wake gpio\n" ); |
196 | |
197 | input = devm_input_allocate_device(dev); |
198 | if (!input) |
199 | return -ENOMEM; |
200 | |
201 | input->name = client->name; |
202 | input->id.bustype = BUS_I2C; |
203 | input->open = icn8318_start; |
204 | input->close = icn8318_stop; |
205 | input->dev.parent = dev; |
206 | |
207 | input_set_capability(dev: input, EV_ABS, ABS_MT_POSITION_X); |
208 | input_set_capability(dev: input, EV_ABS, ABS_MT_POSITION_Y); |
209 | |
210 | touchscreen_parse_properties(input, multitouch: true, prop: &data->prop); |
211 | if (!input_abs_get_max(dev: input, ABS_MT_POSITION_X) || |
212 | !input_abs_get_max(dev: input, ABS_MT_POSITION_Y)) { |
213 | dev_err(dev, "Error touchscreen-size-x and/or -y missing\n" ); |
214 | return -EINVAL; |
215 | } |
216 | |
217 | error = input_mt_init_slots(dev: input, ICN8318_MAX_TOUCHES, |
218 | INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); |
219 | if (error) |
220 | return error; |
221 | |
222 | data->client = client; |
223 | data->input = input; |
224 | input_set_drvdata(dev: input, data); |
225 | |
226 | error = devm_request_threaded_irq(dev, irq: client->irq, NULL, thread_fn: icn8318_irq, |
227 | IRQF_ONESHOT, devname: client->name, dev_id: data); |
228 | if (error) { |
229 | dev_err(dev, "Error requesting irq: %d\n" , error); |
230 | return error; |
231 | } |
232 | |
233 | /* Stop device till opened */ |
234 | icn8318_stop(dev: data->input); |
235 | |
236 | error = input_register_device(input); |
237 | if (error) |
238 | return error; |
239 | |
240 | i2c_set_clientdata(client, data); |
241 | |
242 | return 0; |
243 | } |
244 | |
245 | static const struct of_device_id icn8318_of_match[] = { |
246 | { .compatible = "chipone,icn8318" }, |
247 | { } |
248 | }; |
249 | MODULE_DEVICE_TABLE(of, icn8318_of_match); |
250 | |
251 | /* This is useless for OF-enabled devices, but it is needed by I2C subsystem */ |
252 | static const struct i2c_device_id icn8318_i2c_id[] = { |
253 | { }, |
254 | }; |
255 | MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id); |
256 | |
257 | static struct i2c_driver icn8318_driver = { |
258 | .driver = { |
259 | .name = "chipone_icn8318" , |
260 | .pm = pm_sleep_ptr(&icn8318_pm_ops), |
261 | .of_match_table = icn8318_of_match, |
262 | }, |
263 | .probe = icn8318_probe, |
264 | .id_table = icn8318_i2c_id, |
265 | }; |
266 | |
267 | module_i2c_driver(icn8318_driver); |
268 | |
269 | MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver" ); |
270 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
271 | MODULE_LICENSE("GPL" ); |
272 | |