1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/module.h>
18#include <linux/moduleparam.h>
19#include <linux/kernel.h>
20#include <linux/input.h>
21#include <linux/delay.h>
22#include <linux/bitops.h>
23#include <linux/wm97xx.h>
24
25#define TS_NAME "wm97xx"
26#define WM9705_VERSION "1.00"
27#define DEFAULT_PRESSURE 0xb0c0
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43static int pil;
44module_param(pil, int, 0);
45MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
46
47
48
49
50
51
52static int pressure = DEFAULT_PRESSURE & 0xfff;
53module_param(pressure, int, 0);
54MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
55
56
57
58
59
60
61
62
63
64
65
66
67
68static int delay = 4;
69module_param(delay, int, 0);
70MODULE_PARM_DESC(delay, "Set adc sample delay.");
71
72
73
74
75
76
77
78
79
80
81
82static int pdd = 8;
83module_param(pdd, int, 0);
84MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99static int mask;
100module_param(mask, int, 0);
101MODULE_PARM_DESC(mask, "Set adc mask function.");
102
103
104
105
106static const int delay_table[] = {
107 21,
108 42,
109 84,
110 167,
111 333,
112 667,
113 1000,
114 1333,
115 2000,
116 2667,
117 3333,
118 4000,
119 4667,
120 5333,
121 6000,
122 0
123};
124
125
126
127
128
129
130static inline void poll_delay(int d)
131{
132 udelay(3 * AC97_LINK_FRAME + delay_table[d]);
133}
134
135
136
137
138static void wm9705_phy_init(struct wm97xx *wm)
139{
140 u16 dig1 = 0, dig2 = WM97XX_RPR;
141
142
143
144
145
146 wm97xx_reg_write(wm, AC97_AUX, 0x8000);
147 wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
148
149
150 if (pil == 2) {
151 dig2 |= WM9705_PIL;
152 dev_dbg(wm->dev,
153 "setting pressure measurement current to 400uA.");
154 } else if (pil)
155 dev_dbg(wm->dev,
156 "setting pressure measurement current to 200uA.");
157 if (!pil)
158 pressure = 0;
159
160
161 if (delay != 4) {
162 if (delay < 0 || delay > 15) {
163 dev_dbg(wm->dev, "supplied delay out of range.");
164 delay = 4;
165 }
166 }
167 dig1 &= 0xff0f;
168 dig1 |= WM97XX_DELAY(delay);
169 dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
170 delay_table[delay]);
171
172
173 dig2 |= (pdd & 0x000f);
174 dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
175
176
177 dig2 |= ((mask & 0x3) << 4);
178
179 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
180 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
181}
182
183static void wm9705_dig_enable(struct wm97xx *wm, int enable)
184{
185 if (enable) {
186 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
187 wm->dig[2] | WM97XX_PRP_DET_DIG);
188 wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
189 } else
190 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
191 wm->dig[2] & ~WM97XX_PRP_DET_DIG);
192}
193
194static void wm9705_aux_prepare(struct wm97xx *wm)
195{
196 memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
197 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
198 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
199}
200
201static void wm9705_dig_restore(struct wm97xx *wm)
202{
203 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
204 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
205}
206
207static inline int is_pden(struct wm97xx *wm)
208{
209 return wm->dig[2] & WM9705_PDEN;
210}
211
212
213
214
215static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
216{
217 int timeout = 5 * delay;
218 bool wants_pen = adcsel & WM97XX_PEN_DOWN;
219
220 if (wants_pen && !wm->pen_probably_down) {
221 u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
222 if (!(data & WM97XX_PEN_DOWN))
223 return RC_PENUP;
224 wm->pen_probably_down = 1;
225 }
226
227
228 if (wm->mach_ops && wm->mach_ops->pre_sample)
229 wm->mach_ops->pre_sample(adcsel);
230 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
231 | WM97XX_POLL | WM97XX_DELAY(delay));
232
233
234 poll_delay(delay);
235
236
237 while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
238 && timeout) {
239 udelay(AC97_LINK_FRAME);
240 timeout--;
241 }
242
243 if (timeout == 0) {
244
245 if (is_pden(wm))
246 wm->pen_probably_down = 0;
247 else
248 dev_dbg(wm->dev, "adc sample timeout");
249 return RC_PENUP;
250 }
251
252 *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
253 if (wm->mach_ops && wm->mach_ops->post_sample)
254 wm->mach_ops->post_sample(adcsel);
255
256
257 if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
258 dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
259 adcsel & WM97XX_ADCSEL_MASK,
260 *sample & WM97XX_ADCSEL_MASK);
261 return RC_PENUP;
262 }
263
264 if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
265 wm->pen_probably_down = 0;
266 return RC_PENUP;
267 }
268
269 return RC_VALID;
270}
271
272
273
274
275static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
276{
277 int rc;
278
279 rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
280 if (rc != RC_VALID)
281 return rc;
282 rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
283 if (rc != RC_VALID)
284 return rc;
285 if (pil) {
286 rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);
287 if (rc != RC_VALID)
288 return rc;
289 } else
290 data->p = DEFAULT_PRESSURE;
291
292 return RC_VALID;
293}
294
295
296
297
298
299static int wm9705_acc_enable(struct wm97xx *wm, int enable)
300{
301 u16 dig1, dig2;
302 int ret = 0;
303
304 dig1 = wm->dig[1];
305 dig2 = wm->dig[2];
306
307 if (enable) {
308
309 if (wm->mach_ops->acc_startup &&
310 (ret = wm->mach_ops->acc_startup(wm)) < 0)
311 return ret;
312 dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
313 WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
314 dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
315 WM97XX_DELAY(delay) |
316 WM97XX_SLT(wm->acc_slot) |
317 WM97XX_RATE(wm->acc_rate);
318 if (pil)
319 dig1 |= WM97XX_ADCSEL_PRES;
320 dig2 |= WM9705_PDEN;
321 } else {
322 dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
323 dig2 &= ~WM9705_PDEN;
324 if (wm->mach_ops->acc_shutdown)
325 wm->mach_ops->acc_shutdown(wm);
326 }
327
328 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
329 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
330
331 return ret;
332}
333
334struct wm97xx_codec_drv wm9705_codec = {
335 .id = WM9705_ID2,
336 .name = "wm9705",
337 .poll_sample = wm9705_poll_sample,
338 .poll_touch = wm9705_poll_touch,
339 .acc_enable = wm9705_acc_enable,
340 .phy_init = wm9705_phy_init,
341 .dig_enable = wm9705_dig_enable,
342 .dig_restore = wm9705_dig_restore,
343 .aux_prepare = wm9705_aux_prepare,
344};
345EXPORT_SYMBOL_GPL(wm9705_codec);
346
347
348MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
349MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
350MODULE_LICENSE("GPL");
351