2 * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
4 * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood <lrg@slimlogic.co.uk>
6 * Parts Copyright : Ian Molton <spyro@f2s.com>
7 * Andrew Zabolotny <zap@homelink.ru>
8 * Russell King <rmk@arm.linux.org.uk>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
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>
25 #define TS_NAME "wm97xx"
26 #define WM9705_VERSION "1.00"
27 #define DEFAULT_PRESSURE 0xb0c0
34 * Set current used for pressure measurement.
36 * Set pil = 2 to use 400uA
37 * pil = 1 to use 200uA and
38 * pil = 0 to disable pressure measurement.
40 * This is used to increase the range of values returned by the adc
41 * when measureing touchpanel pressure.
44 module_param(pil
, int, 0);
45 MODULE_PARM_DESC(pil
, "Set current used for pressure measurement.");
48 * Set threshold for pressure measurement.
50 * Pen down pressure below threshold is ignored.
52 static int pressure
= DEFAULT_PRESSURE
& 0xfff;
53 module_param(pressure
, int, 0);
54 MODULE_PARM_DESC(pressure
, "Set threshold for pressure measurement.");
57 * Set adc sample delay.
59 * For accurate touchpanel measurements, some settling time may be
60 * required between the switch matrix applying a voltage across the
61 * touchpanel plate and the ADC sampling the signal.
63 * This delay can be set by setting delay = n, where n is the array
64 * position of the delay in the array delay_table below.
65 * Long delays > 1ms are supported for completeness, but are not
69 module_param(delay
, int, 0);
70 MODULE_PARM_DESC(delay
, "Set adc sample delay.");
73 * Pen detect comparator threshold.
75 * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
76 * i.e. 1 = Vmid/15 threshold
77 * 15 = Vmid/1 threshold
79 * Adjust this value if you are having problems with pen detect not
80 * detecting any down events.
83 module_param(pdd
, int, 0);
84 MODULE_PARM_DESC(pdd
, "Set pen detect comparator threshold");
87 * Set adc mask function.
89 * Sources of glitch noise, such as signals driving an LCD display, may feed
90 * through to the touch screen plates and affect measurement accuracy. In
91 * order to minimise this, a signal may be applied to the MASK pin to delay or
92 * synchronise the sampling.
94 * 0 = No delay or sync
95 * 1 = High on pin stops conversions
96 * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
97 * 3 = Edge triggered, edge on pin starts conversion after delay param
100 module_param(mask
, int, 0);
101 MODULE_PARM_DESC(mask
, "Set adc mask function.");
104 * ADC sample delay times in uS
106 static const int delay_table
[] = {
107 21, /* 1 AC97 Link frames */
122 0 /* No delay, switch matrix always on */
126 * Delay after issuing a POLL command.
128 * The delay is 3 AC97 link frames + the touchpanel settling delay
130 static inline void poll_delay(int d
)
132 udelay(3 * AC97_LINK_FRAME
+ delay_table
[d
]);
136 * set up the physical settings of the WM9705
138 static void wm9705_phy_init(struct wm97xx
*wm
)
140 u16 dig1
= 0, dig2
= WM97XX_RPR
;
143 * mute VIDEO and AUX as they share X and Y touchscreen
144 * inputs on the WM9705
146 wm97xx_reg_write(wm
, AC97_AUX
, 0x8000);
147 wm97xx_reg_write(wm
, AC97_VIDEO
, 0x8000);
149 /* touchpanel pressure current*/
153 "setting pressure measurement current to 400uA.");
156 "setting pressure measurement current to 200uA.");
160 /* polling mode sample settling delay */
162 if (delay
< 0 || delay
> 15) {
163 dev_dbg(wm
->dev
, "supplied delay out of range.");
168 dig1
|= WM97XX_DELAY(delay
);
169 dev_dbg(wm
->dev
, "setting adc sample delay to %d u Secs.",
173 dig2
|= (pdd
& 0x000f);
174 dev_dbg(wm
->dev
, "setting pdd to Vmid/%d", 1 - (pdd
& 0x000f));
177 dig2
|= ((mask
& 0x3) << 4);
179 wm97xx_reg_write(wm
, AC97_WM97XX_DIGITISER1
, dig1
);
180 wm97xx_reg_write(wm
, AC97_WM97XX_DIGITISER2
, dig2
);
183 static void wm9705_dig_enable(struct wm97xx
*wm
, int 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
); /* dummy read */
190 wm97xx_reg_write(wm
, AC97_WM97XX_DIGITISER2
,
191 wm
->dig
[2] & ~WM97XX_PRP_DET_DIG
);
194 static void wm9705_aux_prepare(struct wm97xx
*wm
)
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
);
201 static void wm9705_dig_restore(struct wm97xx
*wm
)
203 wm97xx_reg_write(wm
, AC97_WM97XX_DIGITISER1
, wm
->dig_save
[1]);
204 wm97xx_reg_write(wm
, AC97_WM97XX_DIGITISER2
, wm
->dig_save
[2]);
207 static inline int is_pden(struct wm97xx
*wm
)
209 return wm
->dig
[2] & WM9705_PDEN
;
213 * Read a sample from the WM9705 adc in polling mode.
215 static int wm9705_poll_sample(struct wm97xx
*wm
, int adcsel
, int *sample
)
217 int timeout
= 5 * delay
;
219 if (!wm
->pen_probably_down
) {
220 u16 data
= wm97xx_reg_read(wm
, AC97_WM97XX_DIGITISER_RD
);
221 if (!(data
& WM97XX_PEN_DOWN
))
223 wm
->pen_probably_down
= 1;
226 /* set up digitiser */
228 adcsel
= ((adcsel
& 0x7fff) + 3) << 12;
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
));
235 /* wait 3 AC97 time slots + delay for conversion */
238 /* wait for POLL to go low */
239 while ((wm97xx_reg_read(wm
, AC97_WM97XX_DIGITISER1
) & WM97XX_POLL
)
241 udelay(AC97_LINK_FRAME
);
246 /* If PDEN is set, we can get a timeout when pen goes up */
248 wm
->pen_probably_down
= 0;
250 dev_dbg(wm
->dev
, "adc sample timeout");
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
);
258 /* check we have correct sample */
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
);
265 if (!(*sample
& WM97XX_PEN_DOWN
)) {
266 wm
->pen_probably_down
= 0;
274 * Sample the WM9705 touchscreen in polling mode
276 static int wm9705_poll_touch(struct wm97xx
*wm
, struct wm97xx_data
*data
)
280 rc
= wm9705_poll_sample(wm
, WM97XX_ADCSEL_X
, &data
->x
);
283 rc
= wm9705_poll_sample(wm
, WM97XX_ADCSEL_Y
, &data
->y
);
287 rc
= wm9705_poll_sample(wm
, WM97XX_ADCSEL_PRES
, &data
->p
);
291 data
->p
= DEFAULT_PRESSURE
;
297 * Enable WM9705 continuous mode, i.e. touch data is streamed across
300 static int wm9705_acc_enable(struct wm97xx
*wm
, int enable
)
309 /* continuous mode */
310 if (wm
->mach_ops
->acc_startup
&&
311 (ret
= wm
->mach_ops
->acc_startup(wm
)) < 0)
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
);
320 dig1
|= WM97XX_ADCSEL_PRES
;
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
);
329 wm97xx_reg_write(wm
, AC97_WM97XX_DIGITISER1
, dig1
);
330 wm97xx_reg_write(wm
, AC97_WM97XX_DIGITISER2
, dig2
);
335 struct wm97xx_codec_drv wm9705_codec
= {
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
,
346 EXPORT_SYMBOL_GPL(wm9705_codec
);
348 /* Module information */
349 MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
350 MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
351 MODULE_LICENSE("GPL");