1
2
3
4
5
6
7
8
9
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
26
27
28
29
30
31
32
33
34
35
36
37
38static int pil;
39module_param(pil, int, 0);
40MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
41
42
43
44
45
46
47static int pressure = DEFAULT_PRESSURE & 0xfff;
48module_param(pressure, int, 0);
49MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
50
51
52
53
54
55
56
57
58
59
60
61
62
63static int delay = 4;
64module_param(delay, int, 0);
65MODULE_PARM_DESC(delay, "Set adc sample delay.");
66
67
68
69
70
71
72
73
74
75
76
77static int pdd = 8;
78module_param(pdd, int, 0);
79MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94static int mask;
95module_param(mask, int, 0);
96MODULE_PARM_DESC(mask, "Set adc mask function.");
97
98
99
100
101static const int delay_table[] = {
102 21,
103 42,
104 84,
105 167,
106 333,
107 667,
108 1000,
109 1333,
110 2000,
111 2667,
112 3333,
113 4000,
114 4667,
115 5333,
116 6000,
117 0
118};
119
120
121
122
123
124
125static inline void poll_delay(int d)
126{
127 udelay(3 * AC97_LINK_FRAME + delay_table[d]);
128}
129
130
131
132
133static void wm9705_phy_init(struct wm97xx *wm)
134{
135 u16 dig1 = 0, dig2 = WM97XX_RPR;
136
137
138
139
140
141 wm97xx_reg_write(wm, AC97_AUX, 0x8000);
142 wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
143
144
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
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
168 dig2 |= (pdd & 0x000f);
169 dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
170
171
172 dig2 |= ((mask & 0x3) << 4);
173
174 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
175 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
176}
177
178static void wm9705_dig_enable(struct wm97xx *wm, int enable)
179{
180 if (enable) {
181 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
182 wm->dig[2] | WM97XX_PRP_DET_DIG);
183 wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
184 } else
185 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
186 wm->dig[2] & ~WM97XX_PRP_DET_DIG);
187}
188
189static 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, 0);
193 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
194}
195
196static void wm9705_dig_restore(struct wm97xx *wm)
197{
198 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
199 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
200}
201
202static inline int is_pden(struct wm97xx *wm)
203{
204 return wm->dig[2] & WM9705_PDEN;
205}
206
207
208
209
210static 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
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, (adcsel & WM97XX_ADCSEL_MASK)
226 | WM97XX_POLL | WM97XX_DELAY(delay));
227
228
229 poll_delay(delay);
230
231
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
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
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
269
270static 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, &data->x);
275 if (rc != RC_VALID)
276 return rc;
277 rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &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, &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
292
293
294static 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
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, dig1);
324 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
325
326 return ret;
327}
328
329struct 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};
340EXPORT_SYMBOL_GPL(wm9705_codec);
341
342
343MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
344MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
345MODULE_LICENSE("GPL");
346