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
219 if (!wm->pen_probably_down) {
220 u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
221 if (!(data & WM97XX_PEN_DOWN))
222 return RC_PENUP;
223 wm->pen_probably_down = 1;
224 }
225
226
227 if (adcsel & 0x8000)
228 adcsel = ((adcsel & 0x7fff) + 3) << 12;
229
230 if (wm->mach_ops && wm->mach_ops->pre_sample)
231 wm->mach_ops->pre_sample(adcsel);
232 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
233 adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
234
235
236 poll_delay(delay);
237
238
239 while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
240 && timeout) {
241 udelay(AC97_LINK_FRAME);
242 timeout--;
243 }
244
245 if (timeout == 0) {
246
247 if (is_pden(wm))
248 wm->pen_probably_down = 0;
249 else
250 dev_dbg(wm->dev, "adc sample timeout");
251 return RC_PENUP;
252 }
253
254 *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
255 if (wm->mach_ops && wm->mach_ops->post_sample)
256 wm->mach_ops->post_sample(adcsel);
257
258
259 if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
260 dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
261 *sample & WM97XX_ADCSEL_MASK);
262 return RC_PENUP;
263 }
264
265 if (!(*sample & WM97XX_PEN_DOWN)) {
266 wm->pen_probably_down = 0;
267 return RC_PENUP;
268 }
269
270 return RC_VALID;
271}
272
273
274
275
276static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
277{
278 int rc;
279
280 rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
281 if (rc != RC_VALID)
282 return rc;
283 rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
284 if (rc != RC_VALID)
285 return rc;
286 if (pil) {
287 rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p);
288 if (rc != RC_VALID)
289 return rc;
290 } else
291 data->p = DEFAULT_PRESSURE;
292
293 return RC_VALID;
294}
295
296
297
298
299
300static int wm9705_acc_enable(struct wm97xx *wm, int enable)
301{
302 u16 dig1, dig2;
303 int ret = 0;
304
305 dig1 = wm->dig[1];
306 dig2 = wm->dig[2];
307
308 if (enable) {
309
310 if (wm->mach_ops->acc_startup &&
311 (ret = wm->mach_ops->acc_startup(wm)) < 0)
312 return ret;
313 dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
314 WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
315 dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
316 WM97XX_DELAY(delay) |
317 WM97XX_SLT(wm->acc_slot) |
318 WM97XX_RATE(wm->acc_rate);
319 if (pil)
320 dig1 |= WM97XX_ADCSEL_PRES;
321 dig2 |= WM9705_PDEN;
322 } else {
323 dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
324 dig2 &= ~WM9705_PDEN;
325 if (wm->mach_ops->acc_shutdown)
326 wm->mach_ops->acc_shutdown(wm);
327 }
328
329 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
330 wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
331
332 return ret;
333}
334
335struct wm97xx_codec_drv wm9705_codec = {
336 .id = WM9705_ID2,
337 .name = "wm9705",
338 .poll_sample = wm9705_poll_sample,
339 .poll_touch = wm9705_poll_touch,
340 .acc_enable = wm9705_acc_enable,
341 .phy_init = wm9705_phy_init,
342 .dig_enable = wm9705_dig_enable,
343 .dig_restore = wm9705_dig_restore,
344 .aux_prepare = wm9705_aux_prepare,
345};
346EXPORT_SYMBOL_GPL(wm9705_codec);
347
348
349MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
350MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
351MODULE_LICENSE("GPL");
352