1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. |
4 | * |
5 | * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. |
6 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
7 | * Parts Copyright : Ian Molton <spyro@f2s.com> |
8 | * Andrew Zabolotny <zap@homelink.ru> |
9 | * Russell King <rmk@arm.linux.org.uk> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/moduleparam.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/input.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/bitops.h> |
18 | #include <linux/wm97xx.h> |
19 | |
20 | #define TS_NAME "wm97xx" |
21 | #define WM9713_VERSION "1.00" |
22 | #define DEFAULT_PRESSURE 0xb0c0 |
23 | |
24 | /* |
25 | * Module parameters |
26 | */ |
27 | |
28 | /* |
29 | * Set internal pull up for pen detect. |
30 | * |
31 | * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) |
32 | * i.e. pull up resistance = 64k Ohms / rpu. |
33 | * |
34 | * Adjust this value if you are having problems with pen detect not |
35 | * detecting any down event. |
36 | */ |
37 | static int rpu = 8; |
38 | module_param(rpu, int, 0); |
39 | MODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect." ); |
40 | |
41 | /* |
42 | * Set current used for pressure measurement. |
43 | * |
44 | * Set pil = 2 to use 400uA |
45 | * pil = 1 to use 200uA and |
46 | * pil = 0 to disable pressure measurement. |
47 | * |
48 | * This is used to increase the range of values returned by the adc |
49 | * when measureing touchpanel pressure. |
50 | */ |
51 | static int pil; |
52 | module_param(pil, int, 0); |
53 | MODULE_PARM_DESC(pil, "Set current used for pressure measurement." ); |
54 | |
55 | /* |
56 | * Set threshold for pressure measurement. |
57 | * |
58 | * Pen down pressure below threshold is ignored. |
59 | */ |
60 | static int pressure = DEFAULT_PRESSURE & 0xfff; |
61 | module_param(pressure, int, 0); |
62 | MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement." ); |
63 | |
64 | /* |
65 | * Set adc sample delay. |
66 | * |
67 | * For accurate touchpanel measurements, some settling time may be |
68 | * required between the switch matrix applying a voltage across the |
69 | * touchpanel plate and the ADC sampling the signal. |
70 | * |
71 | * This delay can be set by setting delay = n, where n is the array |
72 | * position of the delay in the array delay_table below. |
73 | * Long delays > 1ms are supported for completeness, but are not |
74 | * recommended. |
75 | */ |
76 | static int delay = 4; |
77 | module_param(delay, int, 0); |
78 | MODULE_PARM_DESC(delay, "Set adc sample delay." ); |
79 | |
80 | /* |
81 | * Set five_wire = 1 to use a 5 wire touchscreen. |
82 | * |
83 | * NOTE: Five wire mode does not allow for readback of pressure. |
84 | */ |
85 | static int five_wire; |
86 | module_param(five_wire, int, 0); |
87 | MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen." ); |
88 | |
89 | /* |
90 | * Set adc mask function. |
91 | * |
92 | * Sources of glitch noise, such as signals driving an LCD display, may feed |
93 | * through to the touch screen plates and affect measurement accuracy. In |
94 | * order to minimise this, a signal may be applied to the MASK pin to delay or |
95 | * synchronise the sampling. |
96 | * |
97 | * 0 = No delay or sync |
98 | * 1 = High on pin stops conversions |
99 | * 2 = Edge triggered, edge on pin delays conversion by delay param (above) |
100 | * 3 = Edge triggered, edge on pin starts conversion after delay param |
101 | */ |
102 | static int mask; |
103 | module_param(mask, int, 0); |
104 | MODULE_PARM_DESC(mask, "Set adc mask function." ); |
105 | |
106 | /* |
107 | * Coordinate Polling Enable. |
108 | * |
109 | * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together |
110 | * for every poll. |
111 | */ |
112 | static int coord; |
113 | module_param(coord, int, 0); |
114 | MODULE_PARM_DESC(coord, "Polling coordinate mode" ); |
115 | |
116 | /* |
117 | * ADC sample delay times in uS |
118 | */ |
119 | static const int delay_table[] = { |
120 | 21, /* 1 AC97 Link frames */ |
121 | 42, /* 2 */ |
122 | 84, /* 4 */ |
123 | 167, /* 8 */ |
124 | 333, /* 16 */ |
125 | 667, /* 32 */ |
126 | 1000, /* 48 */ |
127 | 1333, /* 64 */ |
128 | 2000, /* 96 */ |
129 | 2667, /* 128 */ |
130 | 3333, /* 160 */ |
131 | 4000, /* 192 */ |
132 | 4667, /* 224 */ |
133 | 5333, /* 256 */ |
134 | 6000, /* 288 */ |
135 | 0 /* No delay, switch matrix always on */ |
136 | }; |
137 | |
138 | /* |
139 | * Delay after issuing a POLL command. |
140 | * |
141 | * The delay is 3 AC97 link frames + the touchpanel settling delay |
142 | */ |
143 | static inline void poll_delay(int d) |
144 | { |
145 | udelay(3 * AC97_LINK_FRAME + delay_table[d]); |
146 | } |
147 | |
148 | /* |
149 | * set up the physical settings of the WM9713 |
150 | */ |
151 | static void wm9713_phy_init(struct wm97xx *wm) |
152 | { |
153 | u16 dig1 = 0, dig2, dig3; |
154 | |
155 | /* default values */ |
156 | dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); |
157 | dig3 = WM9712_RPU(1); |
158 | |
159 | /* rpu */ |
160 | if (rpu) { |
161 | dig3 &= 0xffc0; |
162 | dig3 |= WM9712_RPU(rpu); |
163 | dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n" , |
164 | 64000 / rpu); |
165 | } |
166 | |
167 | /* Five wire panel? */ |
168 | if (five_wire) { |
169 | dig3 |= WM9713_45W; |
170 | dev_info(wm->dev, "setting 5-wire touchscreen mode." ); |
171 | |
172 | if (pil) { |
173 | dev_warn(wm->dev, |
174 | "Pressure measurement not supported in 5 " |
175 | "wire mode, disabling\n" ); |
176 | pil = 0; |
177 | } |
178 | } |
179 | |
180 | /* touchpanel pressure */ |
181 | if (pil == 2) { |
182 | dig3 |= WM9712_PIL; |
183 | dev_info(wm->dev, |
184 | "setting pressure measurement current to 400uA." ); |
185 | } else if (pil) |
186 | dev_info(wm->dev, |
187 | "setting pressure measurement current to 200uA." ); |
188 | if (!pil) |
189 | pressure = 0; |
190 | |
191 | /* sample settling delay */ |
192 | if (delay < 0 || delay > 15) { |
193 | dev_info(wm->dev, "supplied delay out of range." ); |
194 | delay = 4; |
195 | dev_info(wm->dev, "setting adc sample delay to %d u Secs." , |
196 | delay_table[delay]); |
197 | } |
198 | dig2 &= 0xff0f; |
199 | dig2 |= WM97XX_DELAY(delay); |
200 | |
201 | /* mask */ |
202 | dig3 |= ((mask & 0x3) << 4); |
203 | if (coord) |
204 | dig3 |= WM9713_WAIT; |
205 | |
206 | wm->misc = wm97xx_reg_read(wm, reg: 0x5a); |
207 | |
208 | wm97xx_reg_write(wm, AC97_WM9713_DIG1, val: dig1); |
209 | wm97xx_reg_write(wm, AC97_WM9713_DIG2, val: dig2); |
210 | wm97xx_reg_write(wm, AC97_WM9713_DIG3, val: dig3); |
211 | wm97xx_reg_write(wm, AC97_GPIO_STICKY, val: 0x0); |
212 | } |
213 | |
214 | static void wm9713_dig_enable(struct wm97xx *wm, int enable) |
215 | { |
216 | u16 val; |
217 | |
218 | if (enable) { |
219 | val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); |
220 | wm97xx_reg_write(wm, AC97_EXTENDED_MID, val: val & 0x7fff); |
221 | wm97xx_reg_write(wm, AC97_WM9713_DIG3, val: wm->dig[2] | |
222 | WM97XX_PRP_DET_DIG); |
223 | wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ |
224 | } else { |
225 | wm97xx_reg_write(wm, AC97_WM9713_DIG3, val: wm->dig[2] & |
226 | ~WM97XX_PRP_DET_DIG); |
227 | val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); |
228 | wm97xx_reg_write(wm, AC97_EXTENDED_MID, val: val | 0x8000); |
229 | } |
230 | } |
231 | |
232 | static void wm9713_dig_restore(struct wm97xx *wm) |
233 | { |
234 | wm97xx_reg_write(wm, AC97_WM9713_DIG1, val: wm->dig_save[0]); |
235 | wm97xx_reg_write(wm, AC97_WM9713_DIG2, val: wm->dig_save[1]); |
236 | wm97xx_reg_write(wm, AC97_WM9713_DIG3, val: wm->dig_save[2]); |
237 | } |
238 | |
239 | static void wm9713_aux_prepare(struct wm97xx *wm) |
240 | { |
241 | memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); |
242 | wm97xx_reg_write(wm, AC97_WM9713_DIG1, val: 0); |
243 | wm97xx_reg_write(wm, AC97_WM9713_DIG2, val: 0); |
244 | wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); |
245 | } |
246 | |
247 | static inline int is_pden(struct wm97xx *wm) |
248 | { |
249 | return wm->dig[2] & WM9713_PDEN; |
250 | } |
251 | |
252 | /* |
253 | * Read a sample from the WM9713 adc in polling mode. |
254 | */ |
255 | static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample) |
256 | { |
257 | u16 dig1; |
258 | int timeout = 5 * delay; |
259 | bool wants_pen = adcsel & WM97XX_PEN_DOWN; |
260 | |
261 | if (wants_pen && !wm->pen_probably_down) { |
262 | u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
263 | if (!(data & WM97XX_PEN_DOWN)) |
264 | return RC_PENUP; |
265 | wm->pen_probably_down = 1; |
266 | } |
267 | |
268 | /* set up digitiser */ |
269 | dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); |
270 | dig1 &= ~WM9713_ADCSEL_MASK; |
271 | /* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */ |
272 | dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12); |
273 | |
274 | if (wm->mach_ops && wm->mach_ops->pre_sample) |
275 | wm->mach_ops->pre_sample(adcsel); |
276 | wm97xx_reg_write(wm, AC97_WM9713_DIG1, val: dig1 | WM9713_POLL); |
277 | |
278 | /* wait 3 AC97 time slots + delay for conversion */ |
279 | poll_delay(d: delay); |
280 | |
281 | /* wait for POLL to go low */ |
282 | while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && |
283 | timeout) { |
284 | udelay(AC97_LINK_FRAME); |
285 | timeout--; |
286 | } |
287 | |
288 | if (timeout <= 0) { |
289 | /* If PDEN is set, we can get a timeout when pen goes up */ |
290 | if (is_pden(wm)) |
291 | wm->pen_probably_down = 0; |
292 | else |
293 | dev_dbg(wm->dev, "adc sample timeout" ); |
294 | return RC_PENUP; |
295 | } |
296 | |
297 | *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
298 | if (wm->mach_ops && wm->mach_ops->post_sample) |
299 | wm->mach_ops->post_sample(adcsel); |
300 | |
301 | /* check we have correct sample */ |
302 | if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { |
303 | dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x" , |
304 | adcsel & WM97XX_ADCSEL_MASK, |
305 | *sample & WM97XX_ADCSEL_MASK); |
306 | return RC_PENUP; |
307 | } |
308 | |
309 | if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { |
310 | wm->pen_probably_down = 0; |
311 | return RC_PENUP; |
312 | } |
313 | |
314 | return RC_VALID; |
315 | } |
316 | |
317 | /* |
318 | * Read a coordinate from the WM9713 adc in polling mode. |
319 | */ |
320 | static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) |
321 | { |
322 | u16 dig1; |
323 | int timeout = 5 * delay; |
324 | |
325 | if (!wm->pen_probably_down) { |
326 | u16 val = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
327 | if (!(val & WM97XX_PEN_DOWN)) |
328 | return RC_PENUP; |
329 | wm->pen_probably_down = 1; |
330 | } |
331 | |
332 | /* set up digitiser */ |
333 | dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); |
334 | dig1 &= ~WM9713_ADCSEL_MASK; |
335 | if (pil) |
336 | dig1 |= WM9713_ADCSEL_PRES; |
337 | |
338 | if (wm->mach_ops && wm->mach_ops->pre_sample) |
339 | wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); |
340 | wm97xx_reg_write(wm, AC97_WM9713_DIG1, |
341 | val: dig1 | WM9713_POLL | WM9713_COO); |
342 | |
343 | /* wait 3 AC97 time slots + delay for conversion */ |
344 | poll_delay(d: delay); |
345 | data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
346 | /* wait for POLL to go low */ |
347 | while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) |
348 | && timeout) { |
349 | udelay(AC97_LINK_FRAME); |
350 | timeout--; |
351 | } |
352 | |
353 | if (timeout <= 0) { |
354 | /* If PDEN is set, we can get a timeout when pen goes up */ |
355 | if (is_pden(wm)) |
356 | wm->pen_probably_down = 0; |
357 | else |
358 | dev_dbg(wm->dev, "adc sample timeout" ); |
359 | return RC_PENUP; |
360 | } |
361 | |
362 | /* read back data */ |
363 | data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
364 | if (pil) |
365 | data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
366 | else |
367 | data->p = DEFAULT_PRESSURE; |
368 | |
369 | if (wm->mach_ops && wm->mach_ops->post_sample) |
370 | wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); |
371 | |
372 | /* check we have correct sample */ |
373 | if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) |
374 | goto err; |
375 | if (pil && !(data->p & WM97XX_ADCSEL_PRES)) |
376 | goto err; |
377 | |
378 | if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) { |
379 | wm->pen_probably_down = 0; |
380 | return RC_PENUP; |
381 | } |
382 | return RC_VALID; |
383 | err: |
384 | return 0; |
385 | } |
386 | |
387 | /* |
388 | * Sample the WM9713 touchscreen in polling mode |
389 | */ |
390 | static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) |
391 | { |
392 | int rc; |
393 | |
394 | if (coord) { |
395 | rc = wm9713_poll_coord(wm, data); |
396 | if (rc != RC_VALID) |
397 | return rc; |
398 | } else { |
399 | rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, sample: &data->x); |
400 | if (rc != RC_VALID) |
401 | return rc; |
402 | rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, sample: &data->y); |
403 | if (rc != RC_VALID) |
404 | return rc; |
405 | if (pil) { |
406 | rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, |
407 | sample: &data->p); |
408 | if (rc != RC_VALID) |
409 | return rc; |
410 | } else |
411 | data->p = DEFAULT_PRESSURE; |
412 | } |
413 | return RC_VALID; |
414 | } |
415 | |
416 | /* |
417 | * Enable WM9713 continuous mode, i.e. touch data is streamed across |
418 | * an AC97 slot |
419 | */ |
420 | static int wm9713_acc_enable(struct wm97xx *wm, int enable) |
421 | { |
422 | u16 dig1, dig2, dig3; |
423 | int ret = 0; |
424 | |
425 | dig1 = wm->dig[0]; |
426 | dig2 = wm->dig[1]; |
427 | dig3 = wm->dig[2]; |
428 | |
429 | if (enable) { |
430 | /* continuous mode */ |
431 | if (wm->mach_ops->acc_startup && |
432 | (ret = wm->mach_ops->acc_startup(wm)) < 0) |
433 | return ret; |
434 | |
435 | dig1 &= ~WM9713_ADCSEL_MASK; |
436 | dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | |
437 | WM9713_ADCSEL_Y; |
438 | if (pil) |
439 | dig1 |= WM9713_ADCSEL_PRES; |
440 | dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | |
441 | WM97XX_CM_RATE_MASK); |
442 | dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) | |
443 | WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate); |
444 | dig3 |= WM9713_PDEN; |
445 | } else { |
446 | dig1 &= ~(WM9713_CTC | WM9713_COO); |
447 | dig2 &= ~WM97XX_SLEN; |
448 | dig3 &= ~WM9713_PDEN; |
449 | if (wm->mach_ops->acc_shutdown) |
450 | wm->mach_ops->acc_shutdown(wm); |
451 | } |
452 | |
453 | wm97xx_reg_write(wm, AC97_WM9713_DIG1, val: dig1); |
454 | wm97xx_reg_write(wm, AC97_WM9713_DIG2, val: dig2); |
455 | wm97xx_reg_write(wm, AC97_WM9713_DIG3, val: dig3); |
456 | |
457 | return ret; |
458 | } |
459 | |
460 | struct wm97xx_codec_drv wm9713_codec = { |
461 | .id = WM9713_ID2, |
462 | .name = "wm9713" , |
463 | .poll_sample = wm9713_poll_sample, |
464 | .poll_touch = wm9713_poll_touch, |
465 | .acc_enable = wm9713_acc_enable, |
466 | .phy_init = wm9713_phy_init, |
467 | .dig_enable = wm9713_dig_enable, |
468 | .dig_restore = wm9713_dig_restore, |
469 | .aux_prepare = wm9713_aux_prepare, |
470 | }; |
471 | EXPORT_SYMBOL_GPL(wm9713_codec); |
472 | |
473 | /* Module information */ |
474 | MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>" ); |
475 | MODULE_DESCRIPTION("WM9713 Touch Screen Driver" ); |
476 | MODULE_LICENSE("GPL" ); |
477 | |