1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. |
4 | * |
5 | * Copyright 2003, 2004, 2005, 2006, 2007 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 WM9705_VERSION "1.00" |
22 | #define DEFAULT_PRESSURE 0xb0c0 |
23 | |
24 | /* |
25 | * Module parameters |
26 | */ |
27 | |
28 | /* |
29 | * Set current used for pressure measurement. |
30 | * |
31 | * Set pil = 2 to use 400uA |
32 | * pil = 1 to use 200uA and |
33 | * pil = 0 to disable pressure measurement. |
34 | * |
35 | * This is used to increase the range of values returned by the adc |
36 | * when measureing touchpanel pressure. |
37 | */ |
38 | static int pil; |
39 | module_param(pil, int, 0); |
40 | MODULE_PARM_DESC(pil, "Set current used for pressure measurement." ); |
41 | |
42 | /* |
43 | * Set threshold for pressure measurement. |
44 | * |
45 | * Pen down pressure below threshold is ignored. |
46 | */ |
47 | static int pressure = DEFAULT_PRESSURE & 0xfff; |
48 | module_param(pressure, int, 0); |
49 | MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement." ); |
50 | |
51 | /* |
52 | * Set adc sample delay. |
53 | * |
54 | * For accurate touchpanel measurements, some settling time may be |
55 | * required between the switch matrix applying a voltage across the |
56 | * touchpanel plate and the ADC sampling the signal. |
57 | * |
58 | * This delay can be set by setting delay = n, where n is the array |
59 | * position of the delay in the array delay_table below. |
60 | * Long delays > 1ms are supported for completeness, but are not |
61 | * recommended. |
62 | */ |
63 | static int delay = 4; |
64 | module_param(delay, int, 0); |
65 | MODULE_PARM_DESC(delay, "Set adc sample delay." ); |
66 | |
67 | /* |
68 | * Pen detect comparator threshold. |
69 | * |
70 | * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold |
71 | * i.e. 1 = Vmid/15 threshold |
72 | * 15 = Vmid/1 threshold |
73 | * |
74 | * Adjust this value if you are having problems with pen detect not |
75 | * detecting any down events. |
76 | */ |
77 | static int pdd = 8; |
78 | module_param(pdd, int, 0); |
79 | MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold" ); |
80 | |
81 | /* |
82 | * Set adc mask function. |
83 | * |
84 | * Sources of glitch noise, such as signals driving an LCD display, may feed |
85 | * through to the touch screen plates and affect measurement accuracy. In |
86 | * order to minimise this, a signal may be applied to the MASK pin to delay or |
87 | * synchronise the sampling. |
88 | * |
89 | * 0 = No delay or sync |
90 | * 1 = High on pin stops conversions |
91 | * 2 = Edge triggered, edge on pin delays conversion by delay param (above) |
92 | * 3 = Edge triggered, edge on pin starts conversion after delay param |
93 | */ |
94 | static int mask; |
95 | module_param(mask, int, 0); |
96 | MODULE_PARM_DESC(mask, "Set adc mask function." ); |
97 | |
98 | /* |
99 | * ADC sample delay times in uS |
100 | */ |
101 | static const int delay_table[] = { |
102 | 21, /* 1 AC97 Link frames */ |
103 | 42, /* 2 */ |
104 | 84, /* 4 */ |
105 | 167, /* 8 */ |
106 | 333, /* 16 */ |
107 | 667, /* 32 */ |
108 | 1000, /* 48 */ |
109 | 1333, /* 64 */ |
110 | 2000, /* 96 */ |
111 | 2667, /* 128 */ |
112 | 3333, /* 160 */ |
113 | 4000, /* 192 */ |
114 | 4667, /* 224 */ |
115 | 5333, /* 256 */ |
116 | 6000, /* 288 */ |
117 | 0 /* No delay, switch matrix always on */ |
118 | }; |
119 | |
120 | /* |
121 | * Delay after issuing a POLL command. |
122 | * |
123 | * The delay is 3 AC97 link frames + the touchpanel settling delay |
124 | */ |
125 | static inline void poll_delay(int d) |
126 | { |
127 | udelay(3 * AC97_LINK_FRAME + delay_table[d]); |
128 | } |
129 | |
130 | /* |
131 | * set up the physical settings of the WM9705 |
132 | */ |
133 | static void wm9705_phy_init(struct wm97xx *wm) |
134 | { |
135 | u16 dig1 = 0, dig2 = WM97XX_RPR; |
136 | |
137 | /* |
138 | * mute VIDEO and AUX as they share X and Y touchscreen |
139 | * inputs on the WM9705 |
140 | */ |
141 | wm97xx_reg_write(wm, AC97_AUX, val: 0x8000); |
142 | wm97xx_reg_write(wm, AC97_VIDEO, val: 0x8000); |
143 | |
144 | /* touchpanel pressure current*/ |
145 | if (pil == 2) { |
146 | dig2 |= WM9705_PIL; |
147 | dev_dbg(wm->dev, |
148 | "setting pressure measurement current to 400uA." ); |
149 | } else if (pil) |
150 | dev_dbg(wm->dev, |
151 | "setting pressure measurement current to 200uA." ); |
152 | if (!pil) |
153 | pressure = 0; |
154 | |
155 | /* polling mode sample settling delay */ |
156 | if (delay != 4) { |
157 | if (delay < 0 || delay > 15) { |
158 | dev_dbg(wm->dev, "supplied delay out of range." ); |
159 | delay = 4; |
160 | } |
161 | } |
162 | dig1 &= 0xff0f; |
163 | dig1 |= WM97XX_DELAY(delay); |
164 | dev_dbg(wm->dev, "setting adc sample delay to %d u Secs." , |
165 | delay_table[delay]); |
166 | |
167 | /* WM9705 pdd */ |
168 | dig2 |= (pdd & 0x000f); |
169 | dev_dbg(wm->dev, "setting pdd to Vmid/%d" , 1 - (pdd & 0x000f)); |
170 | |
171 | /* mask */ |
172 | dig2 |= ((mask & 0x3) << 4); |
173 | |
174 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, val: dig1); |
175 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, val: dig2); |
176 | } |
177 | |
178 | static void wm9705_dig_enable(struct wm97xx *wm, int enable) |
179 | { |
180 | if (enable) { |
181 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, |
182 | val: wm->dig[2] | WM97XX_PRP_DET_DIG); |
183 | wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ |
184 | } else |
185 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, |
186 | val: wm->dig[2] & ~WM97XX_PRP_DET_DIG); |
187 | } |
188 | |
189 | static void wm9705_aux_prepare(struct wm97xx *wm) |
190 | { |
191 | memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); |
192 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, val: 0); |
193 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); |
194 | } |
195 | |
196 | static void wm9705_dig_restore(struct wm97xx *wm) |
197 | { |
198 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, val: wm->dig_save[1]); |
199 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, val: wm->dig_save[2]); |
200 | } |
201 | |
202 | static inline int is_pden(struct wm97xx *wm) |
203 | { |
204 | return wm->dig[2] & WM9705_PDEN; |
205 | } |
206 | |
207 | /* |
208 | * Read a sample from the WM9705 adc in polling mode. |
209 | */ |
210 | static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample) |
211 | { |
212 | int timeout = 5 * delay; |
213 | bool wants_pen = adcsel & WM97XX_PEN_DOWN; |
214 | |
215 | if (wants_pen && !wm->pen_probably_down) { |
216 | u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
217 | if (!(data & WM97XX_PEN_DOWN)) |
218 | return RC_PENUP; |
219 | wm->pen_probably_down = 1; |
220 | } |
221 | |
222 | /* set up digitiser */ |
223 | if (wm->mach_ops && wm->mach_ops->pre_sample) |
224 | wm->mach_ops->pre_sample(adcsel); |
225 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, val: (adcsel & WM97XX_ADCSEL_MASK) |
226 | | WM97XX_POLL | WM97XX_DELAY(delay)); |
227 | |
228 | /* wait 3 AC97 time slots + delay for conversion */ |
229 | poll_delay(d: delay); |
230 | |
231 | /* wait for POLL to go low */ |
232 | while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) |
233 | && timeout) { |
234 | udelay(AC97_LINK_FRAME); |
235 | timeout--; |
236 | } |
237 | |
238 | if (timeout == 0) { |
239 | /* If PDEN is set, we can get a timeout when pen goes up */ |
240 | if (is_pden(wm)) |
241 | wm->pen_probably_down = 0; |
242 | else |
243 | dev_dbg(wm->dev, "adc sample timeout" ); |
244 | return RC_PENUP; |
245 | } |
246 | |
247 | *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); |
248 | if (wm->mach_ops && wm->mach_ops->post_sample) |
249 | wm->mach_ops->post_sample(adcsel); |
250 | |
251 | /* check we have correct sample */ |
252 | if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { |
253 | dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x" , |
254 | adcsel & WM97XX_ADCSEL_MASK, |
255 | *sample & WM97XX_ADCSEL_MASK); |
256 | return RC_PENUP; |
257 | } |
258 | |
259 | if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { |
260 | wm->pen_probably_down = 0; |
261 | return RC_PENUP; |
262 | } |
263 | |
264 | return RC_VALID; |
265 | } |
266 | |
267 | /* |
268 | * Sample the WM9705 touchscreen in polling mode |
269 | */ |
270 | static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) |
271 | { |
272 | int rc; |
273 | |
274 | rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, sample: &data->x); |
275 | if (rc != RC_VALID) |
276 | return rc; |
277 | rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, sample: &data->y); |
278 | if (rc != RC_VALID) |
279 | return rc; |
280 | if (pil) { |
281 | rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, sample: &data->p); |
282 | if (rc != RC_VALID) |
283 | return rc; |
284 | } else |
285 | data->p = DEFAULT_PRESSURE; |
286 | |
287 | return RC_VALID; |
288 | } |
289 | |
290 | /* |
291 | * Enable WM9705 continuous mode, i.e. touch data is streamed across |
292 | * an AC97 slot |
293 | */ |
294 | static int wm9705_acc_enable(struct wm97xx *wm, int enable) |
295 | { |
296 | u16 dig1, dig2; |
297 | int ret = 0; |
298 | |
299 | dig1 = wm->dig[1]; |
300 | dig2 = wm->dig[2]; |
301 | |
302 | if (enable) { |
303 | /* continuous mode */ |
304 | if (wm->mach_ops->acc_startup && |
305 | (ret = wm->mach_ops->acc_startup(wm)) < 0) |
306 | return ret; |
307 | dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | |
308 | WM97XX_DELAY_MASK | WM97XX_SLT_MASK); |
309 | dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | |
310 | WM97XX_DELAY(delay) | |
311 | WM97XX_SLT(wm->acc_slot) | |
312 | WM97XX_RATE(wm->acc_rate); |
313 | if (pil) |
314 | dig1 |= WM97XX_ADCSEL_PRES; |
315 | dig2 |= WM9705_PDEN; |
316 | } else { |
317 | dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); |
318 | dig2 &= ~WM9705_PDEN; |
319 | if (wm->mach_ops->acc_shutdown) |
320 | wm->mach_ops->acc_shutdown(wm); |
321 | } |
322 | |
323 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, val: dig1); |
324 | wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, val: dig2); |
325 | |
326 | return ret; |
327 | } |
328 | |
329 | struct wm97xx_codec_drv wm9705_codec = { |
330 | .id = WM9705_ID2, |
331 | .name = "wm9705" , |
332 | .poll_sample = wm9705_poll_sample, |
333 | .poll_touch = wm9705_poll_touch, |
334 | .acc_enable = wm9705_acc_enable, |
335 | .phy_init = wm9705_phy_init, |
336 | .dig_enable = wm9705_dig_enable, |
337 | .dig_restore = wm9705_dig_restore, |
338 | .aux_prepare = wm9705_aux_prepare, |
339 | }; |
340 | EXPORT_SYMBOL_GPL(wm9705_codec); |
341 | |
342 | /* Module information */ |
343 | MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>" ); |
344 | MODULE_DESCRIPTION("WM9705 Touch Screen Driver" ); |
345 | MODULE_LICENSE("GPL" ); |
346 | |