1 |
1275 |
phoenix |
/*
|
2 |
|
|
* ac97_plugin_wm97xx.c -- Touch screen driver for Wolfson WM9705 and WM9712
|
3 |
|
|
* AC97 Codecs.
|
4 |
|
|
*
|
5 |
|
|
* Copyright 2003 Wolfson Microelectronics PLC.
|
6 |
|
|
* Author: Liam Girdwood
|
7 |
|
|
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
|
8 |
|
|
*
|
9 |
|
|
* This program is free software; you can redistribute it and/or modify it
|
10 |
|
|
* under the terms of the GNU General Public License as published by the
|
11 |
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
12 |
|
|
* option) any later version.
|
13 |
|
|
*
|
14 |
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15 |
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16 |
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
17 |
|
|
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
18 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
19 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
20 |
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
21 |
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
22 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
23 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24 |
|
|
*
|
25 |
|
|
* You should have received a copy of the GNU General Public License along
|
26 |
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
27 |
|
|
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
28 |
|
|
*
|
29 |
|
|
* Notes:
|
30 |
|
|
*
|
31 |
|
|
* Features:
|
32 |
|
|
* - supports WM9705, WM9712
|
33 |
|
|
* - polling mode
|
34 |
|
|
* - coordinate polling
|
35 |
|
|
* - adjustable rpu/dpp settings
|
36 |
|
|
* - adjustable pressure current
|
37 |
|
|
* - adjustable sample settle delay
|
38 |
|
|
* - 4 and 5 wire touchscreens (5 wire is WM9712 only)
|
39 |
|
|
* - pen down detection
|
40 |
|
|
* - battery monitor
|
41 |
|
|
* - sample AUX adc's
|
42 |
|
|
* - power management
|
43 |
|
|
* - direct AC97 IO from userspace (#define WM97XX_TS_DEBUG)
|
44 |
|
|
*
|
45 |
|
|
* TODO:
|
46 |
|
|
* - continuous mode
|
47 |
|
|
* - adjustable sample rate
|
48 |
|
|
* - AUX adc in coordinate / continous modes
|
49 |
|
|
* - Official device identifier or misc device ?
|
50 |
|
|
*
|
51 |
|
|
* Revision history
|
52 |
|
|
* 7th May 2003 Initial version.
|
53 |
|
|
* 6th June 2003 Added non module support and AC97 registration.
|
54 |
|
|
* 18th June 2003 Added AUX adc sampling.
|
55 |
|
|
* 23rd June 2003 Did some minimal reformatting, fixed a couple of
|
56 |
|
|
* locking bugs and noted a race to fix.
|
57 |
|
|
* 24th June 2003 Added power management and fixed race condition.
|
58 |
|
|
*/
|
59 |
|
|
|
60 |
|
|
#include <linux/module.h>
|
61 |
|
|
#include <linux/version.h>
|
62 |
|
|
#include <linux/kernel.h>
|
63 |
|
|
#include <linux/init.h>
|
64 |
|
|
#include <linux/fs.h>
|
65 |
|
|
#include <linux/delay.h>
|
66 |
|
|
#include <linux/poll.h>
|
67 |
|
|
#include <linux/string.h>
|
68 |
|
|
#include <linux/proc_fs.h>
|
69 |
|
|
#include <linux/miscdevice.h>
|
70 |
|
|
#include <linux/pm.h>
|
71 |
|
|
#include <linux/wm97xx.h> /* WM97xx registers and bits */
|
72 |
|
|
#include <asm/uaccess.h> /* get_user,copy_to_user */
|
73 |
|
|
#include <asm/io.h>
|
74 |
|
|
|
75 |
|
|
#define TS_NAME "ac97_plugin_wm97xx"
|
76 |
|
|
#define TS_MINOR 16
|
77 |
|
|
#define WM_TS_VERSION "0.6"
|
78 |
|
|
#define AC97_NUM_REG 64
|
79 |
|
|
|
80 |
|
|
|
81 |
|
|
/*
|
82 |
|
|
* Debug
|
83 |
|
|
*/
|
84 |
|
|
|
85 |
|
|
#define PFX TS_NAME
|
86 |
|
|
#define WM97XX_TS_DEBUG 0
|
87 |
|
|
|
88 |
|
|
#ifdef WM97XX_TS_DEBUG
|
89 |
|
|
#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
|
90 |
|
|
#else
|
91 |
|
|
#define dbg(format, arg...) do {} while (0)
|
92 |
|
|
#endif
|
93 |
|
|
#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
|
94 |
|
|
#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
|
95 |
|
|
#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
|
96 |
|
|
|
97 |
|
|
/*
|
98 |
|
|
* Module parameters
|
99 |
|
|
*/
|
100 |
|
|
|
101 |
|
|
|
102 |
|
|
/*
|
103 |
|
|
* Set the codec sample mode.
|
104 |
|
|
*
|
105 |
|
|
* The WM9712 can sample touchscreen data in 3 different operating
|
106 |
|
|
* modes. i.e. polling, coordinate and continous.
|
107 |
|
|
*
|
108 |
|
|
* Polling:- The driver polls the codec and issues 3 seperate commands
|
109 |
|
|
* over the AC97 link to read X,Y and pressure.
|
110 |
|
|
*
|
111 |
|
|
* Coordinate: - The driver polls the codec and only issues 1 command over
|
112 |
|
|
* the AC97 link to read X,Y and pressure. This mode has
|
113 |
|
|
* strict timing requirements and may drop samples if
|
114 |
|
|
* interrupted. However, it is less demanding on the AC97
|
115 |
|
|
* link. Note: this mode requires a larger delay than polling
|
116 |
|
|
* mode.
|
117 |
|
|
*
|
118 |
|
|
* Continuous:- The codec automatically samples X,Y and pressure and then
|
119 |
|
|
* sends the data over the AC97 link in slots. This is the
|
120 |
|
|
* same method used by the codec when recording audio.
|
121 |
|
|
*
|
122 |
|
|
* Set mode = 0 for polling, 1 for coordinate and 2 for continuous.
|
123 |
|
|
*
|
124 |
|
|
*/
|
125 |
|
|
MODULE_PARM(mode,"i");
|
126 |
|
|
MODULE_PARM_DESC(mode, "Set WM97XX operation mode");
|
127 |
|
|
static int mode = 0;
|
128 |
|
|
|
129 |
|
|
/*
|
130 |
|
|
* WM9712 - Set internal pull up for pen detect.
|
131 |
|
|
*
|
132 |
|
|
* Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
|
133 |
|
|
* i.e. pull up resistance = 64k Ohms / rpu.
|
134 |
|
|
*
|
135 |
|
|
* Adjust this value if you are having problems with pen detect not
|
136 |
|
|
* detecting any down events.
|
137 |
|
|
*/
|
138 |
|
|
MODULE_PARM(rpu,"i");
|
139 |
|
|
MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
|
140 |
|
|
static int rpu = 0;
|
141 |
|
|
|
142 |
|
|
/*
|
143 |
|
|
* WM9705 - Pen detect comparator threshold.
|
144 |
|
|
*
|
145 |
|
|
* 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
|
146 |
|
|
* i.e. 1 = Vmid/15 threshold
|
147 |
|
|
* 15 = Vmid/1 threshold
|
148 |
|
|
*
|
149 |
|
|
* Adjust this value if you are having problems with pen detect not
|
150 |
|
|
* detecting any down events.
|
151 |
|
|
*/
|
152 |
|
|
MODULE_PARM(pdd,"i");
|
153 |
|
|
MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
|
154 |
|
|
static int pdd = 0;
|
155 |
|
|
|
156 |
|
|
/*
|
157 |
|
|
* Set current used for pressure measurement.
|
158 |
|
|
*
|
159 |
|
|
* Set pil = 2 to use 400uA
|
160 |
|
|
* pil = 1 to use 200uA and
|
161 |
|
|
* pil = 0 to disable pressure measurement.
|
162 |
|
|
*
|
163 |
|
|
* This is used to increase the range of values returned by the adc
|
164 |
|
|
* when measureing touchpanel pressure.
|
165 |
|
|
*/
|
166 |
|
|
MODULE_PARM(pil,"i");
|
167 |
|
|
MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
|
168 |
|
|
static int pil = 0;
|
169 |
|
|
|
170 |
|
|
/*
|
171 |
|
|
* WM9712 - Set five_wire = 1 to use a 5 wire touchscreen.
|
172 |
|
|
*
|
173 |
|
|
* NOTE: Five wire mode does not allow for readback of pressure.
|
174 |
|
|
*/
|
175 |
|
|
MODULE_PARM(five_wire,"i");
|
176 |
|
|
MODULE_PARM_DESC(five_wire, "Set 5 wire touchscreen.");
|
177 |
|
|
static int five_wire = 0;
|
178 |
|
|
|
179 |
|
|
/*
|
180 |
|
|
* Set adc sample delay.
|
181 |
|
|
*
|
182 |
|
|
* For accurate touchpanel measurements, some settling time may be
|
183 |
|
|
* required between the switch matrix applying a voltage across the
|
184 |
|
|
* touchpanel plate and the ADC sampling the signal.
|
185 |
|
|
*
|
186 |
|
|
* This delay can be set by setting delay = n, where n is the array
|
187 |
|
|
* position of the delay in the array delay_table below.
|
188 |
|
|
* Long delays > 1ms are supported for completeness, but are not
|
189 |
|
|
* recommended.
|
190 |
|
|
*/
|
191 |
|
|
MODULE_PARM(delay,"i");
|
192 |
|
|
MODULE_PARM_DESC(delay, "Set adc sample delay.");
|
193 |
|
|
static int delay = 4;
|
194 |
|
|
|
195 |
|
|
|
196 |
|
|
/* +++++++++++++ Lifted from include/linux/h3600_ts.h ++++++++++++++*/
|
197 |
|
|
typedef struct {
|
198 |
|
|
unsigned short pressure; // touch pressure
|
199 |
|
|
unsigned short x; // calibrated X
|
200 |
|
|
unsigned short y; // calibrated Y
|
201 |
|
|
unsigned short millisecs; // timestamp of this event
|
202 |
|
|
} TS_EVENT;
|
203 |
|
|
|
204 |
|
|
typedef struct {
|
205 |
|
|
int xscale;
|
206 |
|
|
int xtrans;
|
207 |
|
|
int yscale;
|
208 |
|
|
int ytrans;
|
209 |
|
|
int xyswap;
|
210 |
|
|
} TS_CAL;
|
211 |
|
|
|
212 |
|
|
/* Use 'f' as magic number */
|
213 |
|
|
#define IOC_MAGIC 'f'
|
214 |
|
|
|
215 |
|
|
#define TS_GET_RATE _IO(IOC_MAGIC, 8)
|
216 |
|
|
#define TS_SET_RATE _IO(IOC_MAGIC, 9)
|
217 |
|
|
#define TS_GET_CAL _IOR(IOC_MAGIC, 10, TS_CAL)
|
218 |
|
|
#define TS_SET_CAL _IOW(IOC_MAGIC, 11, TS_CAL)
|
219 |
|
|
|
220 |
|
|
/* +++++++++++++ Done lifted from include/linux/h3600_ts.h +++++++++*/
|
221 |
|
|
|
222 |
|
|
#define TS_GET_COMP1 _IOR(IOC_MAGIC, 12, short)
|
223 |
|
|
#define TS_GET_COMP2 _IOR(IOC_MAGIC, 13, short)
|
224 |
|
|
#define TS_GET_BMON _IOR(IOC_MAGIC, 14, short)
|
225 |
|
|
#define TS_GET_WIPER _IOR(IOC_MAGIC, 15, short)
|
226 |
|
|
|
227 |
|
|
#ifdef WM97XX_TS_DEBUG
|
228 |
|
|
/* debug get/set ac97 codec register ioctl's */
|
229 |
|
|
#define TS_GET_AC97_REG _IOR(IOC_MAGIC, 20, short)
|
230 |
|
|
#define TS_SET_AC97_REG _IOW(IOC_MAGIC, 21, short)
|
231 |
|
|
#define TS_SET_AC97_INDEX _IOW(IOC_MAGIC, 22, short)
|
232 |
|
|
#endif
|
233 |
|
|
|
234 |
|
|
#define EVENT_BUFSIZE 128
|
235 |
|
|
|
236 |
|
|
typedef struct {
|
237 |
|
|
TS_CAL cal; /* Calibration values */
|
238 |
|
|
TS_EVENT event_buf[EVENT_BUFSIZE];/* The event queue */
|
239 |
|
|
int nextIn, nextOut;
|
240 |
|
|
int event_count;
|
241 |
|
|
int is_wm9712:1; /* are we a WM912 or a WM9705 */
|
242 |
|
|
int is_registered:1; /* Is the driver AC97 registered */
|
243 |
|
|
int line_pgal:5;
|
244 |
|
|
int line_pgar:5;
|
245 |
|
|
int phone_pga:5;
|
246 |
|
|
int mic_pgal:5;
|
247 |
|
|
int mic_pgar:5;
|
248 |
|
|
int overruns; /* event buffer overruns */
|
249 |
|
|
int adc_errs; /* sample read back errors */
|
250 |
|
|
#ifdef WM97XX_TS_DEBUG
|
251 |
|
|
short ac97_index;
|
252 |
|
|
#endif
|
253 |
|
|
struct fasync_struct *fasync; /* asynch notification */
|
254 |
|
|
struct timer_list acq_timer; /* Timer for triggering acquisitions */
|
255 |
|
|
wait_queue_head_t wait; /* read wait queue */
|
256 |
|
|
spinlock_t lock;
|
257 |
|
|
struct ac97_codec *codec;
|
258 |
|
|
struct proc_dir_entry *wm97xx_ts_ps;
|
259 |
|
|
#ifdef WM97XX_TS_DEBUG
|
260 |
|
|
struct proc_dir_entry *wm97xx_debug_ts_ps;
|
261 |
|
|
#endif
|
262 |
|
|
struct pm_dev * pm;
|
263 |
|
|
} wm97xx_ts_t;
|
264 |
|
|
|
265 |
|
|
static inline void poll_delay (void);
|
266 |
|
|
static int __init wm97xx_ts_init_module(void);
|
267 |
|
|
static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample);
|
268 |
|
|
static int wm97xx_coord_read_adc (wm97xx_ts_t* ts, u16* x, u16* y,
|
269 |
|
|
u16* pressure);
|
270 |
|
|
static inline int pendown (wm97xx_ts_t *ts);
|
271 |
|
|
static void wm97xx_acq_timer(unsigned long data);
|
272 |
|
|
static int wm97xx_fasync(int fd, struct file *filp, int mode);
|
273 |
|
|
static int wm97xx_ioctl(struct inode * inode, struct file *filp,
|
274 |
|
|
unsigned int cmd, unsigned long arg);
|
275 |
|
|
static unsigned int wm97xx_poll(struct file * filp, poll_table * wait);
|
276 |
|
|
static ssize_t wm97xx_read(struct file * filp, char * buf, size_t count,
|
277 |
|
|
loff_t * l);
|
278 |
|
|
static int wm97xx_open(struct inode * inode, struct file * filp);
|
279 |
|
|
static int wm97xx_release(struct inode * inode, struct file * filp);
|
280 |
|
|
static void init_wm97xx_phy(void);
|
281 |
|
|
static int adc_get (wm97xx_ts_t *ts, unsigned short *value, int id);
|
282 |
|
|
static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver);
|
283 |
|
|
static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver);
|
284 |
|
|
static void wm97xx_ts_cleanup_module(void);
|
285 |
|
|
static int wm97xx_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data);
|
286 |
|
|
static void wm97xx_suspend(void);
|
287 |
|
|
static void wm97xx_resume(void);
|
288 |
|
|
static void wm9712_pga_save(wm97xx_ts_t* ts);
|
289 |
|
|
static void wm9712_pga_restore(wm97xx_ts_t* ts);
|
290 |
|
|
|
291 |
|
|
/* AC97 registration info */
|
292 |
|
|
static struct ac97_driver wm9705_driver = {
|
293 |
|
|
codec_id: 0x574D4C05,
|
294 |
|
|
codec_mask: 0xFFFFFFFF,
|
295 |
|
|
name: "Wolfson WM9705 Touchscreen/BMON",
|
296 |
|
|
probe: wm97xx_probe,
|
297 |
|
|
remove: __devexit_p(wm97xx_remove),
|
298 |
|
|
};
|
299 |
|
|
|
300 |
|
|
static struct ac97_driver wm9712_driver = {
|
301 |
|
|
codec_id: 0x574D4C12,
|
302 |
|
|
codec_mask: 0xFFFFFFFF,
|
303 |
|
|
name: "Wolfson WM9712 Touchscreen/BMON",
|
304 |
|
|
probe: wm97xx_probe,
|
305 |
|
|
remove: __devexit_p(wm97xx_remove),
|
306 |
|
|
};
|
307 |
|
|
|
308 |
|
|
/* we only support a single touchscreen */
|
309 |
|
|
static wm97xx_ts_t wm97xx_ts;
|
310 |
|
|
|
311 |
|
|
/*
|
312 |
|
|
* ADC sample delay times in uS
|
313 |
|
|
*/
|
314 |
|
|
static const int delay_table[16] = {
|
315 |
|
|
21, // 1 AC97 Link frames
|
316 |
|
|
42, // 2
|
317 |
|
|
84, // 4
|
318 |
|
|
167, // 8
|
319 |
|
|
333, // 16
|
320 |
|
|
667, // 32
|
321 |
|
|
1000, // 48
|
322 |
|
|
1333, // 64
|
323 |
|
|
2000, // 96
|
324 |
|
|
2667, // 128
|
325 |
|
|
3333, // 160
|
326 |
|
|
4000, // 192
|
327 |
|
|
4667, // 224
|
328 |
|
|
5333, // 256
|
329 |
|
|
6000, // 288
|
330 |
|
|
|
331 |
|
|
};
|
332 |
|
|
|
333 |
|
|
/*
|
334 |
|
|
* Delay after issuing a POLL command.
|
335 |
|
|
*
|
336 |
|
|
* The delay is 3 AC97 link frames + the touchpanel settling delay
|
337 |
|
|
*/
|
338 |
|
|
|
339 |
|
|
static inline void poll_delay(void)
|
340 |
|
|
{
|
341 |
|
|
int pdelay = 3 * AC97_LINK_FRAME + delay_table[delay];
|
342 |
|
|
udelay (pdelay);
|
343 |
|
|
}
|
344 |
|
|
|
345 |
|
|
|
346 |
|
|
/*
|
347 |
|
|
* sample the auxillary ADC's
|
348 |
|
|
*/
|
349 |
|
|
|
350 |
|
|
static int adc_get(wm97xx_ts_t* ts, unsigned short * value, int id)
|
351 |
|
|
{
|
352 |
|
|
short adcsel = 0;
|
353 |
|
|
|
354 |
|
|
/* first find out our adcsel flag */
|
355 |
|
|
if (ts->is_wm9712) {
|
356 |
|
|
switch (id) {
|
357 |
|
|
case TS_COMP1:
|
358 |
|
|
adcsel = WM9712_ADCSEL_COMP1;
|
359 |
|
|
break;
|
360 |
|
|
case TS_COMP2:
|
361 |
|
|
adcsel = WM9712_ADCSEL_COMP2;
|
362 |
|
|
break;
|
363 |
|
|
case TS_BMON:
|
364 |
|
|
adcsel = WM9712_ADCSEL_BMON;
|
365 |
|
|
break;
|
366 |
|
|
case TS_WIPER:
|
367 |
|
|
adcsel = WM9712_ADCSEL_WIPER;
|
368 |
|
|
break;
|
369 |
|
|
}
|
370 |
|
|
} else {
|
371 |
|
|
switch (id) {
|
372 |
|
|
case TS_COMP1:
|
373 |
|
|
adcsel = WM9705_ADCSEL_PCBEEP;
|
374 |
|
|
break;
|
375 |
|
|
case TS_COMP2:
|
376 |
|
|
adcsel = WM9705_ADCSEL_PHONE;
|
377 |
|
|
break;
|
378 |
|
|
case TS_BMON:
|
379 |
|
|
adcsel = WM9705_ADCSEL_BMON;
|
380 |
|
|
break;
|
381 |
|
|
case TS_WIPER:
|
382 |
|
|
adcsel = WM9705_ADCSEL_AUX;
|
383 |
|
|
break;
|
384 |
|
|
}
|
385 |
|
|
}
|
386 |
|
|
|
387 |
|
|
/* now sample the adc */
|
388 |
|
|
if (mode == 1) {
|
389 |
|
|
/* coordinate mode - not currently available (TODO) */
|
390 |
|
|
return 0;
|
391 |
|
|
}
|
392 |
|
|
else
|
393 |
|
|
{
|
394 |
|
|
/* polling mode */
|
395 |
|
|
if (!wm97xx_poll_read_adc(ts, adcsel, value))
|
396 |
|
|
return 0;
|
397 |
|
|
}
|
398 |
|
|
|
399 |
|
|
return 1;
|
400 |
|
|
}
|
401 |
|
|
|
402 |
|
|
|
403 |
|
|
/*
|
404 |
|
|
* Read a sample from the adc in polling mode.
|
405 |
|
|
*/
|
406 |
|
|
static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample)
|
407 |
|
|
{
|
408 |
|
|
u16 dig1;
|
409 |
|
|
int timeout = 5 * delay;
|
410 |
|
|
|
411 |
|
|
/* set up digitiser */
|
412 |
|
|
dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1);
|
413 |
|
|
dig1&=0x0fff;
|
414 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | adcsel |
|
415 |
|
|
WM97XX_POLL);
|
416 |
|
|
|
417 |
|
|
/* wait 3 AC97 time slots + delay for conversion */
|
418 |
|
|
poll_delay();
|
419 |
|
|
|
420 |
|
|
/* wait for POLL to go low */
|
421 |
|
|
while ((ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
|
422 |
|
|
udelay(AC97_LINK_FRAME);
|
423 |
|
|
timeout--;
|
424 |
|
|
}
|
425 |
|
|
if (timeout > 0)
|
426 |
|
|
*sample = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
|
427 |
|
|
else {
|
428 |
|
|
ts->adc_errs++;
|
429 |
|
|
err ("adc sample timeout");
|
430 |
|
|
return 0;
|
431 |
|
|
}
|
432 |
|
|
|
433 |
|
|
/* check we have correct sample */
|
434 |
|
|
if ((*sample & 0x7000) != adcsel ) {
|
435 |
|
|
err ("adc wrong sample, read %x got %x", adcsel, *sample & 0x7000);
|
436 |
|
|
return 0;
|
437 |
|
|
}
|
438 |
|
|
return 1;
|
439 |
|
|
}
|
440 |
|
|
|
441 |
|
|
/*
|
442 |
|
|
* Read a sample from the adc in coordinate mode.
|
443 |
|
|
*/
|
444 |
|
|
static int wm97xx_coord_read_adc(wm97xx_ts_t* ts, u16* x, u16* y, u16* pressure)
|
445 |
|
|
{
|
446 |
|
|
u16 dig1;
|
447 |
|
|
int timeout = 5 * delay;
|
448 |
|
|
|
449 |
|
|
/* set up digitiser */
|
450 |
|
|
dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1);
|
451 |
|
|
dig1&=0x0fff;
|
452 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | WM97XX_ADCSEL_PRES |
|
453 |
|
|
WM97XX_POLL);
|
454 |
|
|
|
455 |
|
|
/* wait 3 AC97 time slots + delay for conversion */
|
456 |
|
|
poll_delay();
|
457 |
|
|
|
458 |
|
|
/* read X then wait for 1 AC97 link frame + settling delay */
|
459 |
|
|
*x = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
|
460 |
|
|
udelay (AC97_LINK_FRAME + delay_table[delay]);
|
461 |
|
|
|
462 |
|
|
/* read Y */
|
463 |
|
|
*y = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
|
464 |
|
|
|
465 |
|
|
/* wait for POLL to go low and then read pressure */
|
466 |
|
|
while ((ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)&& timeout) {
|
467 |
|
|
udelay(AC97_LINK_FRAME);
|
468 |
|
|
timeout--;
|
469 |
|
|
}
|
470 |
|
|
if (timeout > 0)
|
471 |
|
|
*pressure = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
|
472 |
|
|
else {
|
473 |
|
|
ts->adc_errs++;
|
474 |
|
|
err ("adc sample timeout");
|
475 |
|
|
return 0;
|
476 |
|
|
}
|
477 |
|
|
|
478 |
|
|
/* check we have correct samples */
|
479 |
|
|
if (((*x & 0x7000) == 0x1000) && ((*y & 0x7000) == 0x2000) &&
|
480 |
|
|
((*pressure & 0x7000) == 0x3000)) {
|
481 |
|
|
return 1;
|
482 |
|
|
} else {
|
483 |
|
|
ts->adc_errs++;
|
484 |
|
|
err ("adc got wrong samples, got x 0x%x y 0x%x pressure 0x%x", *x, *y, *pressure);
|
485 |
|
|
return 0;
|
486 |
|
|
}
|
487 |
|
|
}
|
488 |
|
|
|
489 |
|
|
/*
|
490 |
|
|
* Is the pen down ?
|
491 |
|
|
*/
|
492 |
|
|
static inline int pendown (wm97xx_ts_t *ts)
|
493 |
|
|
{
|
494 |
|
|
return ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN;
|
495 |
|
|
}
|
496 |
|
|
|
497 |
|
|
/*
|
498 |
|
|
* X,Y coordinates and pressure aquisition function.
|
499 |
|
|
* This function is run by a kernel timer and it's frequency between
|
500 |
|
|
* calls is the touchscreen polling rate;
|
501 |
|
|
*/
|
502 |
|
|
|
503 |
|
|
static void wm97xx_acq_timer(unsigned long data)
|
504 |
|
|
{
|
505 |
|
|
wm97xx_ts_t* ts = (wm97xx_ts_t*)data;
|
506 |
|
|
unsigned long flags;
|
507 |
|
|
long x,y;
|
508 |
|
|
TS_EVENT event;
|
509 |
|
|
|
510 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
511 |
|
|
|
512 |
|
|
/* are we still registered ? */
|
513 |
|
|
if (!ts->is_registered) {
|
514 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
515 |
|
|
return; /* we better stop then */
|
516 |
|
|
}
|
517 |
|
|
|
518 |
|
|
/* read coordinates if pen is down */
|
519 |
|
|
if (!pendown(ts))
|
520 |
|
|
goto acq_exit;
|
521 |
|
|
|
522 |
|
|
if (mode == 1) {
|
523 |
|
|
/* coordinate mode */
|
524 |
|
|
if (!wm97xx_coord_read_adc(ts, (u16*)&x, (u16*)&y, &event.pressure))
|
525 |
|
|
goto acq_exit;
|
526 |
|
|
} else
|
527 |
|
|
{
|
528 |
|
|
/* polling mode */
|
529 |
|
|
if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_X, (u16*)&x))
|
530 |
|
|
goto acq_exit;
|
531 |
|
|
if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_Y, (u16*)&y))
|
532 |
|
|
goto acq_exit;
|
533 |
|
|
|
534 |
|
|
/* only read pressure if we have to */
|
535 |
|
|
if (!five_wire && pil) {
|
536 |
|
|
if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_PRES, &event.pressure))
|
537 |
|
|
goto acq_exit;
|
538 |
|
|
}
|
539 |
|
|
else
|
540 |
|
|
event.pressure = 0;
|
541 |
|
|
}
|
542 |
|
|
/* timestamp this new event. */
|
543 |
|
|
event.millisecs = jiffies;
|
544 |
|
|
|
545 |
|
|
/* calibrate and remove unwanted bits from samples */
|
546 |
|
|
event.pressure &= 0x0fff;
|
547 |
|
|
|
548 |
|
|
x &= 0x00000fff;
|
549 |
|
|
x = ((ts->cal.xscale * x) >> 8) + ts->cal.xtrans;
|
550 |
|
|
event.x = (u16)x;
|
551 |
|
|
|
552 |
|
|
y &= 0x00000fff;
|
553 |
|
|
y = ((ts->cal.yscale * y) >> 8) + ts->cal.ytrans;
|
554 |
|
|
event.y = (u16)y;
|
555 |
|
|
|
556 |
|
|
/* add this event to the event queue */
|
557 |
|
|
ts->event_buf[ts->nextIn++] = event;
|
558 |
|
|
if (ts->nextIn == EVENT_BUFSIZE)
|
559 |
|
|
ts->nextIn = 0;
|
560 |
|
|
if (ts->event_count < EVENT_BUFSIZE) {
|
561 |
|
|
ts->event_count++;
|
562 |
|
|
} else {
|
563 |
|
|
/* throw out the oldest event */
|
564 |
|
|
if (++ts->nextOut == EVENT_BUFSIZE) {
|
565 |
|
|
ts->nextOut = 0;
|
566 |
|
|
ts->overruns++;
|
567 |
|
|
}
|
568 |
|
|
}
|
569 |
|
|
|
570 |
|
|
/* async notify */
|
571 |
|
|
if (ts->fasync)
|
572 |
|
|
kill_fasync(&ts->fasync, SIGIO, POLL_IN);
|
573 |
|
|
/* wake up any read call */
|
574 |
|
|
if (waitqueue_active(&ts->wait))
|
575 |
|
|
wake_up_interruptible(&ts->wait);
|
576 |
|
|
|
577 |
|
|
/* schedule next acquire */
|
578 |
|
|
acq_exit:
|
579 |
|
|
ts->acq_timer.expires = jiffies + HZ / 100;
|
580 |
|
|
add_timer(&ts->acq_timer);
|
581 |
|
|
|
582 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
583 |
|
|
}
|
584 |
|
|
|
585 |
|
|
|
586 |
|
|
/* +++++++++++++ File operations ++++++++++++++*/
|
587 |
|
|
|
588 |
|
|
static int wm97xx_fasync(int fd, struct file *filp, int mode)
|
589 |
|
|
{
|
590 |
|
|
wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
|
591 |
|
|
return fasync_helper(fd, filp, mode, &ts->fasync);
|
592 |
|
|
}
|
593 |
|
|
|
594 |
|
|
static int wm97xx_ioctl(struct inode * inode, struct file *filp,
|
595 |
|
|
unsigned int cmd, unsigned long arg)
|
596 |
|
|
{
|
597 |
|
|
unsigned short adc_value;
|
598 |
|
|
#ifdef WM97XX_TS_DEBUG
|
599 |
|
|
short data;
|
600 |
|
|
#endif
|
601 |
|
|
wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
|
602 |
|
|
|
603 |
|
|
switch(cmd) {
|
604 |
|
|
case TS_GET_RATE: /* TODO: what is this? */
|
605 |
|
|
break;
|
606 |
|
|
case TS_SET_RATE: /* TODO: what is this? */
|
607 |
|
|
break;
|
608 |
|
|
case TS_GET_CAL:
|
609 |
|
|
if(copy_to_user((char *)arg, (char *)&ts->cal, sizeof(TS_CAL)))
|
610 |
|
|
return -EFAULT;
|
611 |
|
|
break;
|
612 |
|
|
case TS_SET_CAL:
|
613 |
|
|
if(copy_from_user((char *)&ts->cal, (char *)arg, sizeof(TS_CAL)))
|
614 |
|
|
return -EFAULT;
|
615 |
|
|
break;
|
616 |
|
|
case TS_GET_COMP1:
|
617 |
|
|
if (adc_get(ts, &adc_value, TS_COMP1)) {
|
618 |
|
|
if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
|
619 |
|
|
return -EFAULT;
|
620 |
|
|
}
|
621 |
|
|
else
|
622 |
|
|
return -EIO;
|
623 |
|
|
break;
|
624 |
|
|
case TS_GET_COMP2:
|
625 |
|
|
if (adc_get(ts, &adc_value, TS_COMP2)) {
|
626 |
|
|
if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
|
627 |
|
|
return -EFAULT;
|
628 |
|
|
}
|
629 |
|
|
else
|
630 |
|
|
return -EIO;
|
631 |
|
|
break;
|
632 |
|
|
case TS_GET_BMON:
|
633 |
|
|
if (adc_get(ts, &adc_value, TS_BMON)) {
|
634 |
|
|
if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
|
635 |
|
|
return -EFAULT;
|
636 |
|
|
}
|
637 |
|
|
else
|
638 |
|
|
return -EIO;
|
639 |
|
|
break;
|
640 |
|
|
case TS_GET_WIPER:
|
641 |
|
|
if (adc_get(ts, &adc_value, TS_WIPER)) {
|
642 |
|
|
if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
|
643 |
|
|
return -EFAULT;
|
644 |
|
|
}
|
645 |
|
|
else
|
646 |
|
|
return -EIO;
|
647 |
|
|
break;
|
648 |
|
|
#ifdef WM97XX_TS_DEBUG
|
649 |
|
|
/* debug get/set ac97 codec register ioctl's
|
650 |
|
|
*
|
651 |
|
|
* This is direct IO to the codec registers - BE CAREFULL
|
652 |
|
|
*/
|
653 |
|
|
case TS_GET_AC97_REG: /* read from ac97 reg (index) */
|
654 |
|
|
data = ts->codec->codec_read(ts->codec, ts->ac97_index);
|
655 |
|
|
if(copy_to_user((char *)arg, (char *)&data, sizeof(data)))
|
656 |
|
|
return -EFAULT;
|
657 |
|
|
break;
|
658 |
|
|
case TS_SET_AC97_REG: /* write to ac97 reg (index) */
|
659 |
|
|
if(copy_from_user((char *)&data, (char *)arg, sizeof(data)))
|
660 |
|
|
return -EFAULT;
|
661 |
|
|
ts->codec->codec_write(ts->codec, ts->ac97_index, data);
|
662 |
|
|
break;
|
663 |
|
|
case TS_SET_AC97_INDEX: /* set ac97 reg index */
|
664 |
|
|
if(copy_from_user((char *)&ts->ac97_index, (char *)arg, sizeof(ts->ac97_index)))
|
665 |
|
|
return -EFAULT;
|
666 |
|
|
break;
|
667 |
|
|
#endif
|
668 |
|
|
default:
|
669 |
|
|
return -EINVAL;
|
670 |
|
|
}
|
671 |
|
|
|
672 |
|
|
return 0;
|
673 |
|
|
}
|
674 |
|
|
|
675 |
|
|
static unsigned int wm97xx_poll(struct file * filp, poll_table * wait)
|
676 |
|
|
{
|
677 |
|
|
wm97xx_ts_t *ts = (wm97xx_ts_t *)filp->private_data;
|
678 |
|
|
poll_wait(filp, &ts->wait, wait);
|
679 |
|
|
if (ts->event_count)
|
680 |
|
|
return POLLIN | POLLRDNORM;
|
681 |
|
|
return 0;
|
682 |
|
|
}
|
683 |
|
|
|
684 |
|
|
static ssize_t wm97xx_read(struct file *filp, char *buf, size_t count, loff_t *l)
|
685 |
|
|
{
|
686 |
|
|
wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
|
687 |
|
|
unsigned long flags;
|
688 |
|
|
TS_EVENT event;
|
689 |
|
|
int i;
|
690 |
|
|
|
691 |
|
|
/* are we still registered with AC97 layer ? */
|
692 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
693 |
|
|
if (!ts->is_registered) {
|
694 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
695 |
|
|
return -ENXIO;
|
696 |
|
|
}
|
697 |
|
|
|
698 |
|
|
if (ts->event_count == 0) {
|
699 |
|
|
if (filp->f_flags & O_NONBLOCK)
|
700 |
|
|
return -EAGAIN;
|
701 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
702 |
|
|
|
703 |
|
|
wait_event_interruptible(ts->wait, ts->event_count != 0);
|
704 |
|
|
|
705 |
|
|
/* are we still registered after sleep ? */
|
706 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
707 |
|
|
if (!ts->is_registered) {
|
708 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
709 |
|
|
return -ENXIO;
|
710 |
|
|
}
|
711 |
|
|
if (signal_pending(current))
|
712 |
|
|
return -ERESTARTSYS;
|
713 |
|
|
}
|
714 |
|
|
|
715 |
|
|
for (i = count; i >= sizeof(TS_EVENT);
|
716 |
|
|
i -= sizeof(TS_EVENT), buf += sizeof(TS_EVENT)) {
|
717 |
|
|
if (ts->event_count == 0)
|
718 |
|
|
break;
|
719 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
720 |
|
|
event = ts->event_buf[ts->nextOut++];
|
721 |
|
|
if (ts->nextOut == EVENT_BUFSIZE)
|
722 |
|
|
ts->nextOut = 0;
|
723 |
|
|
if (ts->event_count)
|
724 |
|
|
ts->event_count--;
|
725 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
726 |
|
|
if(copy_to_user(buf, &event, sizeof(TS_EVENT)))
|
727 |
|
|
return i != count ? count - i : -EFAULT;
|
728 |
|
|
}
|
729 |
|
|
return count - i;
|
730 |
|
|
}
|
731 |
|
|
|
732 |
|
|
|
733 |
|
|
static int wm97xx_open(struct inode * inode, struct file * filp)
|
734 |
|
|
{
|
735 |
|
|
wm97xx_ts_t* ts;
|
736 |
|
|
unsigned long flags;
|
737 |
|
|
u16 val;
|
738 |
|
|
int minor = MINOR(inode->i_rdev);
|
739 |
|
|
|
740 |
|
|
if (minor != TS_MINOR)
|
741 |
|
|
return -ENODEV;
|
742 |
|
|
|
743 |
|
|
filp->private_data = ts = &wm97xx_ts;
|
744 |
|
|
|
745 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
746 |
|
|
|
747 |
|
|
/* are we registered with AC97 layer ? */
|
748 |
|
|
if (!ts->is_registered) {
|
749 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
750 |
|
|
return -ENXIO;
|
751 |
|
|
}
|
752 |
|
|
|
753 |
|
|
/* start digitiser */
|
754 |
|
|
val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
|
755 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
|
756 |
|
|
val | WM97XX_PRP_DET_DIG);
|
757 |
|
|
|
758 |
|
|
/* flush event queue */
|
759 |
|
|
ts->nextIn = ts->nextOut = ts->event_count = 0;
|
760 |
|
|
|
761 |
|
|
/* Set up timer. */
|
762 |
|
|
init_timer(&ts->acq_timer);
|
763 |
|
|
ts->acq_timer.function = wm97xx_acq_timer;
|
764 |
|
|
ts->acq_timer.data = (unsigned long)ts;
|
765 |
|
|
ts->acq_timer.expires = jiffies + HZ / 100;
|
766 |
|
|
add_timer(&ts->acq_timer);
|
767 |
|
|
|
768 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
769 |
|
|
return 0;
|
770 |
|
|
}
|
771 |
|
|
|
772 |
|
|
static int wm97xx_release(struct inode * inode, struct file * filp)
|
773 |
|
|
{
|
774 |
|
|
wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
|
775 |
|
|
unsigned long flags;
|
776 |
|
|
u16 val;
|
777 |
|
|
|
778 |
|
|
wm97xx_fasync(-1, filp, 0);
|
779 |
|
|
del_timer_sync(&ts->acq_timer);
|
780 |
|
|
|
781 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
782 |
|
|
|
783 |
|
|
/* stop digitiser */
|
784 |
|
|
val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
|
785 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
|
786 |
|
|
val & ~WM97XX_PRP_DET_DIG);
|
787 |
|
|
|
788 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
789 |
|
|
return 0;
|
790 |
|
|
}
|
791 |
|
|
|
792 |
|
|
static struct file_operations ts_fops = {
|
793 |
|
|
owner: THIS_MODULE,
|
794 |
|
|
read: wm97xx_read,
|
795 |
|
|
poll: wm97xx_poll,
|
796 |
|
|
ioctl: wm97xx_ioctl,
|
797 |
|
|
fasync: wm97xx_fasync,
|
798 |
|
|
open: wm97xx_open,
|
799 |
|
|
release: wm97xx_release,
|
800 |
|
|
};
|
801 |
|
|
|
802 |
|
|
/* +++++++++++++ End File operations ++++++++++++++*/
|
803 |
|
|
|
804 |
|
|
#ifdef CONFIG_PROC_FS
|
805 |
|
|
static int wm97xx_read_proc (char *page, char **start, off_t off,
|
806 |
|
|
int count, int *eof, void *data)
|
807 |
|
|
{
|
808 |
|
|
int len = 0, prpu;
|
809 |
|
|
u16 dig1, dig2, digrd, adcsel, adcsrc, slt, prp, rev;
|
810 |
|
|
unsigned long flags;
|
811 |
|
|
char srev = ' ';
|
812 |
|
|
|
813 |
|
|
wm97xx_ts_t* ts;
|
814 |
|
|
|
815 |
|
|
if ((ts = data) == NULL)
|
816 |
|
|
return -ENODEV;
|
817 |
|
|
|
818 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
819 |
|
|
if (!ts->is_registered) {
|
820 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
821 |
|
|
len += sprintf (page+len, "No device registered\n");
|
822 |
|
|
return len;
|
823 |
|
|
}
|
824 |
|
|
|
825 |
|
|
dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1);
|
826 |
|
|
dig2 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
|
827 |
|
|
digrd = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
|
828 |
|
|
rev = (ts->codec->codec_read(ts->codec, AC97_WM9712_REV) & 0x000c) >> 2;
|
829 |
|
|
|
830 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
831 |
|
|
|
832 |
|
|
adcsel = dig1 & 0x7000;
|
833 |
|
|
adcsrc = digrd & 0x7000;
|
834 |
|
|
slt = (dig1 & 0x7) + 5;
|
835 |
|
|
prp = dig2 & 0xc000;
|
836 |
|
|
prpu = dig2 & 0x003f;
|
837 |
|
|
|
838 |
|
|
/* driver version */
|
839 |
|
|
len += sprintf (page+len, "Wolfson WM97xx Version %s\n", WM_TS_VERSION);
|
840 |
|
|
|
841 |
|
|
/* what we are using */
|
842 |
|
|
len += sprintf (page+len, "Using %s", ts->is_wm9712 ? "WM9712" : "WM9705");
|
843 |
|
|
if (ts->is_wm9712) {
|
844 |
|
|
switch (rev) {
|
845 |
|
|
case 0x0:
|
846 |
|
|
srev = 'A';
|
847 |
|
|
break;
|
848 |
|
|
case 0x1:
|
849 |
|
|
srev = 'B';
|
850 |
|
|
break;
|
851 |
|
|
case 0x2:
|
852 |
|
|
srev = 'D';
|
853 |
|
|
break;
|
854 |
|
|
case 0x3:
|
855 |
|
|
srev = 'E';
|
856 |
|
|
break;
|
857 |
|
|
}
|
858 |
|
|
len += sprintf (page+len, " silicon rev %c\n",srev);
|
859 |
|
|
} else
|
860 |
|
|
len += sprintf (page+len, "\n");
|
861 |
|
|
|
862 |
|
|
/* WM97xx settings */
|
863 |
|
|
len += sprintf (page+len, "Settings :\n%s%s%s%s",
|
864 |
|
|
dig1 & WM97XX_POLL ? " -sampling adc data(poll)\n" : "",
|
865 |
|
|
adcsel == WM97XX_ADCSEL_X ? " -adc set to X coordinate\n" : "",
|
866 |
|
|
adcsel == WM97XX_ADCSEL_Y ? " -adc set to Y coordinate\n" : "",
|
867 |
|
|
adcsel == WM97XX_ADCSEL_PRES ? " -adc set to pressure\n" : "");
|
868 |
|
|
if (ts->is_wm9712) {
|
869 |
|
|
len += sprintf (page+len, "%s%s%s%s",
|
870 |
|
|
adcsel == WM9712_ADCSEL_COMP1 ? " -adc set to COMP1/AUX1\n" : "",
|
871 |
|
|
adcsel == WM9712_ADCSEL_COMP2 ? " -adc set to COMP2/AUX2\n" : "",
|
872 |
|
|
adcsel == WM9712_ADCSEL_BMON ? " -adc set to BMON\n" : "",
|
873 |
|
|
adcsel == WM9712_ADCSEL_WIPER ? " -adc set to WIPER\n" : "");
|
874 |
|
|
} else {
|
875 |
|
|
len += sprintf (page+len, "%s%s%s%s",
|
876 |
|
|
adcsel == WM9705_ADCSEL_PCBEEP ? " -adc set to PCBEEP\n" : "",
|
877 |
|
|
adcsel == WM9705_ADCSEL_PHONE ? " -adc set to PHONE\n" : "",
|
878 |
|
|
adcsel == WM9705_ADCSEL_BMON ? " -adc set to BMON\n" : "",
|
879 |
|
|
adcsel == WM9705_ADCSEL_AUX ? " -adc set to AUX\n" : "");
|
880 |
|
|
}
|
881 |
|
|
|
882 |
|
|
len += sprintf (page+len, "%s%s%s%s%s%s",
|
883 |
|
|
dig1 & WM97XX_COO ? " -coordinate sampling\n" : " -individual sampling\n",
|
884 |
|
|
dig1 & WM97XX_CTC ? " -continuous mode\n" : " -polling mode\n",
|
885 |
|
|
prp == WM97XX_PRP_DET ? " -pen detect enabled, no wake up\n" : "",
|
886 |
|
|
prp == WM97XX_PRP_DETW ? " -pen detect enabled, wake up\n" : "",
|
887 |
|
|
prp == WM97XX_PRP_DET_DIG ? " -pen digitiser and pen detect enabled\n" : "",
|
888 |
|
|
dig1 & WM97XX_SLEN ? " -read back using slot " : " -read back using AC97\n");
|
889 |
|
|
|
890 |
|
|
if ((dig1 & WM97XX_SLEN) && slt !=12)
|
891 |
|
|
len += sprintf(page+len, "%d\n", slt);
|
892 |
|
|
len += sprintf (page+len, " -adc sample delay %d uSecs\n", delay_table[(dig1 & 0x00f0) >> 4]);
|
893 |
|
|
|
894 |
|
|
if (ts->is_wm9712) {
|
895 |
|
|
if (prpu)
|
896 |
|
|
len += sprintf (page+len, " -rpu %d Ohms\n", 64000/ prpu);
|
897 |
|
|
len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9712_PIL ? "400" : "200");
|
898 |
|
|
len += sprintf (page+len, " -using %s wire touchscreen mode", dig2 & WM9712_45W ? "5" : "4");
|
899 |
|
|
} else {
|
900 |
|
|
len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9705_PIL ? "400" : "200");
|
901 |
|
|
len += sprintf (page+len, " -%s impedance for PHONE and PCBEEP\n", dig2 & WM9705_PHIZ ? "high" : "low");
|
902 |
|
|
}
|
903 |
|
|
|
904 |
|
|
/* WM97xx digitiser read */
|
905 |
|
|
len += sprintf(page+len, "\nADC data:\n%s%d\n%s%s\n",
|
906 |
|
|
" -adc value (decimal) : ", digrd & 0x0fff,
|
907 |
|
|
" -pen ", digrd & 0x8000 ? "Down" : "Up");
|
908 |
|
|
if (ts->is_wm9712) {
|
909 |
|
|
len += sprintf (page+len, "%s%s%s%s",
|
910 |
|
|
adcsrc == WM9712_ADCSEL_COMP1 ? " -adc value is COMP1/AUX1\n" : "",
|
911 |
|
|
adcsrc == WM9712_ADCSEL_COMP2 ? " -adc value is COMP2/AUX2\n" : "",
|
912 |
|
|
adcsrc == WM9712_ADCSEL_BMON ? " -adc value is BMON\n" : "",
|
913 |
|
|
adcsrc == WM9712_ADCSEL_WIPER ? " -adc value is WIPER\n" : "");
|
914 |
|
|
} else {
|
915 |
|
|
len += sprintf (page+len, "%s%s%s%s",
|
916 |
|
|
adcsrc == WM9705_ADCSEL_PCBEEP ? " -adc value is PCBEEP\n" : "",
|
917 |
|
|
adcsrc == WM9705_ADCSEL_PHONE ? " -adc value is PHONE\n" : "",
|
918 |
|
|
adcsrc == WM9705_ADCSEL_BMON ? " -adc value is BMON\n" : "",
|
919 |
|
|
adcsrc == WM9705_ADCSEL_AUX ? " -adc value is AUX\n" : "");
|
920 |
|
|
}
|
921 |
|
|
|
922 |
|
|
/* register dump */
|
923 |
|
|
len += sprintf(page+len, "\nRegisters:\n%s%x\n%s%x\n%s%x\n",
|
924 |
|
|
" -digitiser 1 (0x76) : 0x", dig1,
|
925 |
|
|
" -digitiser 2 (0x78) : 0x", dig2,
|
926 |
|
|
" -digitiser read (0x7a) : 0x", digrd);
|
927 |
|
|
|
928 |
|
|
/* errors */
|
929 |
|
|
len += sprintf(page+len, "\nErrors:\n%s%d\n%s%d\n",
|
930 |
|
|
" -buffer overruns ", ts->overruns,
|
931 |
|
|
" -coordinate errors ", ts->adc_errs);
|
932 |
|
|
|
933 |
|
|
return len;
|
934 |
|
|
}
|
935 |
|
|
|
936 |
|
|
#ifdef WM97XX_TS_DEBUG
|
937 |
|
|
/* dump all the AC97 register space */
|
938 |
|
|
static int wm_debug_read_proc (char *page, char **start, off_t off,
|
939 |
|
|
int count, int *eof, void *data)
|
940 |
|
|
{
|
941 |
|
|
int len = 0, i;
|
942 |
|
|
unsigned long flags;
|
943 |
|
|
wm97xx_ts_t* ts;
|
944 |
|
|
u16 reg[AC97_NUM_REG];
|
945 |
|
|
|
946 |
|
|
if ((ts = data) == NULL)
|
947 |
|
|
return -ENODEV;
|
948 |
|
|
|
949 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
950 |
|
|
if (!ts->is_registered) {
|
951 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
952 |
|
|
len += sprintf (page+len, "Not registered\n");
|
953 |
|
|
return len;
|
954 |
|
|
}
|
955 |
|
|
|
956 |
|
|
for (i=0; i < AC97_NUM_REG; i++) {
|
957 |
|
|
reg[i] = ts->codec->codec_read(ts->codec, i * 2);
|
958 |
|
|
}
|
959 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
960 |
|
|
|
961 |
|
|
for (i=0; i < AC97_NUM_REG; i++) {
|
962 |
|
|
len += sprintf (page+len, "0x%2.2x : 0x%4.4x\n",i * 2, reg[i]);
|
963 |
|
|
}
|
964 |
|
|
|
965 |
|
|
return len;
|
966 |
|
|
}
|
967 |
|
|
#endif
|
968 |
|
|
|
969 |
|
|
#endif
|
970 |
|
|
|
971 |
|
|
#ifdef CONFIG_PM
|
972 |
|
|
/* WM97xx Power Management
|
973 |
|
|
* The WM9712 has extra powerdown states that are controlled in
|
974 |
|
|
* seperate registers from the AC97 power management.
|
975 |
|
|
* We will only power down into the extra WM9712 states and leave
|
976 |
|
|
* the AC97 power management to the sound driver.
|
977 |
|
|
*/
|
978 |
|
|
static int wm97xx_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data)
|
979 |
|
|
{
|
980 |
|
|
switch(rqst) {
|
981 |
|
|
case PM_SUSPEND:
|
982 |
|
|
wm97xx_suspend();
|
983 |
|
|
break;
|
984 |
|
|
case PM_RESUME:
|
985 |
|
|
wm97xx_resume();
|
986 |
|
|
break;
|
987 |
|
|
}
|
988 |
|
|
return 0;
|
989 |
|
|
}
|
990 |
|
|
|
991 |
|
|
/*
|
992 |
|
|
* Power down the codec
|
993 |
|
|
*/
|
994 |
|
|
static void wm97xx_suspend(void)
|
995 |
|
|
{
|
996 |
|
|
wm97xx_ts_t* ts = &wm97xx_ts;
|
997 |
|
|
u16 reg;
|
998 |
|
|
unsigned long flags;
|
999 |
|
|
|
1000 |
|
|
/* are we registered */
|
1001 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
1002 |
|
|
if (!ts->is_registered) {
|
1003 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1004 |
|
|
return;
|
1005 |
|
|
}
|
1006 |
|
|
|
1007 |
|
|
/* wm9705 does not have extra PM */
|
1008 |
|
|
if (!ts->is_wm9712) {
|
1009 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1010 |
|
|
return;
|
1011 |
|
|
}
|
1012 |
|
|
|
1013 |
|
|
/* save and mute the PGA's */
|
1014 |
|
|
wm9712_pga_save(ts);
|
1015 |
|
|
|
1016 |
|
|
reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
|
1017 |
|
|
ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | 0x001f);
|
1018 |
|
|
|
1019 |
|
|
reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL);
|
1020 |
|
|
ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | 0x1f1f);
|
1021 |
|
|
|
1022 |
|
|
reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
|
1023 |
|
|
ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | 0x1f1f);
|
1024 |
|
|
|
1025 |
|
|
/* power down, dont disable the AC link */
|
1026 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, WM9712_PD(14) | WM9712_PD(13) |
|
1027 |
|
|
WM9712_PD(12) | WM9712_PD(11) | WM9712_PD(10) |
|
1028 |
|
|
WM9712_PD(9) | WM9712_PD(8) | WM9712_PD(7) |
|
1029 |
|
|
WM9712_PD(6) | WM9712_PD(5) | WM9712_PD(4) |
|
1030 |
|
|
WM9712_PD(3) | WM9712_PD(2) | WM9712_PD(1) |
|
1031 |
|
|
WM9712_PD(0));
|
1032 |
|
|
|
1033 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1034 |
|
|
}
|
1035 |
|
|
|
1036 |
|
|
/*
|
1037 |
|
|
* Power up the Codec
|
1038 |
|
|
*/
|
1039 |
|
|
static void wm97xx_resume(void)
|
1040 |
|
|
{
|
1041 |
|
|
wm97xx_ts_t* ts = &wm97xx_ts;
|
1042 |
|
|
unsigned long flags;
|
1043 |
|
|
|
1044 |
|
|
/* are we registered */
|
1045 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
1046 |
|
|
if (!ts->is_registered) {
|
1047 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1048 |
|
|
return;
|
1049 |
|
|
}
|
1050 |
|
|
|
1051 |
|
|
/* wm9705 does not have extra PM */
|
1052 |
|
|
if (!ts->is_wm9712) {
|
1053 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1054 |
|
|
return;
|
1055 |
|
|
}
|
1056 |
|
|
|
1057 |
|
|
/* power up */
|
1058 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, 0x0);
|
1059 |
|
|
|
1060 |
|
|
/* restore PGA state */
|
1061 |
|
|
wm9712_pga_restore(ts);
|
1062 |
|
|
|
1063 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1064 |
|
|
}
|
1065 |
|
|
|
1066 |
|
|
|
1067 |
|
|
/* save state of wm9712 PGA's */
|
1068 |
|
|
static void wm9712_pga_save(wm97xx_ts_t* ts)
|
1069 |
|
|
{
|
1070 |
|
|
ts->phone_pga = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL) & 0x001f;
|
1071 |
|
|
ts->line_pgal = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x1f00;
|
1072 |
|
|
ts->line_pgar = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x001f;
|
1073 |
|
|
ts->mic_pgal = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x1f00;
|
1074 |
|
|
ts->mic_pgar = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x001f;
|
1075 |
|
|
}
|
1076 |
|
|
|
1077 |
|
|
/* restore state of wm9712 PGA's */
|
1078 |
|
|
static void wm9712_pga_restore(wm97xx_ts_t* ts)
|
1079 |
|
|
{
|
1080 |
|
|
u16 reg;
|
1081 |
|
|
|
1082 |
|
|
reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
|
1083 |
|
|
ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | ts->phone_pga);
|
1084 |
|
|
|
1085 |
|
|
reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
|
1086 |
|
|
ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | ts->line_pgar | (ts->line_pgal << 8));
|
1087 |
|
|
|
1088 |
|
|
reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL);
|
1089 |
|
|
ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | ts->mic_pgar | (ts->mic_pgal << 8));
|
1090 |
|
|
}
|
1091 |
|
|
|
1092 |
|
|
#endif
|
1093 |
|
|
|
1094 |
|
|
/*
|
1095 |
|
|
* set up the physical settings of the device
|
1096 |
|
|
*/
|
1097 |
|
|
|
1098 |
|
|
static void init_wm97xx_phy(void)
|
1099 |
|
|
{
|
1100 |
|
|
u16 dig1, dig2, aux, vid;
|
1101 |
|
|
wm97xx_ts_t *ts = &wm97xx_ts;
|
1102 |
|
|
|
1103 |
|
|
/* default values */
|
1104 |
|
|
dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6);
|
1105 |
|
|
if (ts->is_wm9712)
|
1106 |
|
|
dig2 = WM9712_RPU(1);
|
1107 |
|
|
else {
|
1108 |
|
|
dig2 = 0x0;
|
1109 |
|
|
|
1110 |
|
|
/*
|
1111 |
|
|
* mute VIDEO and AUX as they share X and Y touchscreen
|
1112 |
|
|
* inputs on the WM9705
|
1113 |
|
|
*/
|
1114 |
|
|
aux = ts->codec->codec_read(ts->codec, AC97_AUX_VOL);
|
1115 |
|
|
if (!(aux & 0x8000)) {
|
1116 |
|
|
info("muting AUX mixer as it shares X touchscreen coordinate");
|
1117 |
|
|
ts->codec->codec_write(ts->codec, AC97_AUX_VOL, 0x8000 | aux);
|
1118 |
|
|
}
|
1119 |
|
|
|
1120 |
|
|
vid = ts->codec->codec_read(ts->codec, AC97_VIDEO_VOL);
|
1121 |
|
|
if (!(vid & 0x8000)) {
|
1122 |
|
|
info("muting VIDEO mixer as it shares Y touchscreen coordinate");
|
1123 |
|
|
ts->codec->codec_write(ts->codec, AC97_VIDEO_VOL, 0x8000 | vid);
|
1124 |
|
|
}
|
1125 |
|
|
}
|
1126 |
|
|
|
1127 |
|
|
/* WM9712 rpu */
|
1128 |
|
|
if (ts->is_wm9712 && rpu) {
|
1129 |
|
|
dig2 &= 0xffc0;
|
1130 |
|
|
dig2 |= WM9712_RPU(rpu);
|
1131 |
|
|
info("setting pen detect pull-up to %d Ohms",64000 / rpu);
|
1132 |
|
|
}
|
1133 |
|
|
|
1134 |
|
|
/* touchpanel pressure */
|
1135 |
|
|
if (pil == 2) {
|
1136 |
|
|
if (ts->is_wm9712)
|
1137 |
|
|
dig2 |= WM9712_PIL;
|
1138 |
|
|
else
|
1139 |
|
|
dig2 |= WM9705_PIL;
|
1140 |
|
|
info("setting pressure measurement current to 400uA.");
|
1141 |
|
|
} else if (pil)
|
1142 |
|
|
info ("setting pressure measurement current to 200uA.");
|
1143 |
|
|
|
1144 |
|
|
/* WM9712 five wire */
|
1145 |
|
|
if (ts->is_wm9712 && five_wire) {
|
1146 |
|
|
dig2 |= WM9712_45W;
|
1147 |
|
|
info("setting 5-wire touchscreen mode.");
|
1148 |
|
|
}
|
1149 |
|
|
|
1150 |
|
|
/* sample settling delay */
|
1151 |
|
|
if (delay!=4) {
|
1152 |
|
|
if (delay < 0 || delay > 15) {
|
1153 |
|
|
info ("supplied delay out of range.");
|
1154 |
|
|
delay = 4;
|
1155 |
|
|
}
|
1156 |
|
|
dig1 &= 0xff0f;
|
1157 |
|
|
dig1 |= WM97XX_DELAY(delay);
|
1158 |
|
|
info("setting adc sample delay to %d u Secs.", delay_table[delay]);
|
1159 |
|
|
}
|
1160 |
|
|
|
1161 |
|
|
/* coordinate mode */
|
1162 |
|
|
if (mode == 1) {
|
1163 |
|
|
dig1 |= WM97XX_COO;
|
1164 |
|
|
info("using coordinate mode");
|
1165 |
|
|
}
|
1166 |
|
|
|
1167 |
|
|
/* WM9705 pdd */
|
1168 |
|
|
if (pdd && !ts->is_wm9712) {
|
1169 |
|
|
dig2 |= (pdd & 0x000f);
|
1170 |
|
|
info("setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
|
1171 |
|
|
}
|
1172 |
|
|
|
1173 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1);
|
1174 |
|
|
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2, dig2);
|
1175 |
|
|
}
|
1176 |
|
|
|
1177 |
|
|
|
1178 |
|
|
/*
|
1179 |
|
|
* Called by the audio codec initialisation to register
|
1180 |
|
|
* the touchscreen driver.
|
1181 |
|
|
*/
|
1182 |
|
|
|
1183 |
|
|
static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver)
|
1184 |
|
|
{
|
1185 |
|
|
unsigned long flags;
|
1186 |
|
|
u16 id1, id2;
|
1187 |
|
|
wm97xx_ts_t *ts = &wm97xx_ts;
|
1188 |
|
|
|
1189 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
1190 |
|
|
|
1191 |
|
|
/* we only support 1 touchscreen at the moment */
|
1192 |
|
|
if (ts->is_registered) {
|
1193 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1194 |
|
|
return -1;
|
1195 |
|
|
}
|
1196 |
|
|
|
1197 |
|
|
/*
|
1198 |
|
|
* We can only use a WM9705 or WM9712 that has been *first* initialised
|
1199 |
|
|
* by the AC97 audio driver. This is because we have to use the audio
|
1200 |
|
|
* drivers codec read() and write() functions to sample the touchscreen
|
1201 |
|
|
*
|
1202 |
|
|
* If an initialsed WM97xx is found then get the codec read and write
|
1203 |
|
|
* functions.
|
1204 |
|
|
*/
|
1205 |
|
|
|
1206 |
|
|
/* test for a WM9712 or a WM9705 */
|
1207 |
|
|
id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
|
1208 |
|
|
id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
|
1209 |
|
|
if (id1 == WM97XX_ID1 && id2 == WM9712_ID2) {
|
1210 |
|
|
ts->is_wm9712 = 1;
|
1211 |
|
|
info("registered a WM9712");
|
1212 |
|
|
} else if (id1 == WM97XX_ID1 && id2 == WM9705_ID2) {
|
1213 |
|
|
ts->is_wm9712 = 0;
|
1214 |
|
|
info("registered a WM9705");
|
1215 |
|
|
} else {
|
1216 |
|
|
err("could not find a WM97xx codec. Found a 0x%4x:0x%4x instead",
|
1217 |
|
|
id1, id2);
|
1218 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1219 |
|
|
return -1;
|
1220 |
|
|
}
|
1221 |
|
|
|
1222 |
|
|
/* set up AC97 codec interface */
|
1223 |
|
|
ts->codec = codec;
|
1224 |
|
|
codec->driver_private = (void*)&ts;
|
1225 |
|
|
codec->codec_unregister = 0;
|
1226 |
|
|
|
1227 |
|
|
/* set up physical characteristics */
|
1228 |
|
|
init_wm97xx_phy();
|
1229 |
|
|
|
1230 |
|
|
ts->is_registered = 1;
|
1231 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1232 |
|
|
return 0;
|
1233 |
|
|
}
|
1234 |
|
|
|
1235 |
|
|
/* this is called by the audio driver when ac97_codec is unloaded */
|
1236 |
|
|
|
1237 |
|
|
static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver)
|
1238 |
|
|
{
|
1239 |
|
|
unsigned long flags;
|
1240 |
|
|
u16 dig1, dig2;
|
1241 |
|
|
wm97xx_ts_t *ts = codec->driver_private;
|
1242 |
|
|
|
1243 |
|
|
spin_lock_irqsave(&ts->lock, flags);
|
1244 |
|
|
|
1245 |
|
|
/* check that are registered */
|
1246 |
|
|
if (!ts->is_registered) {
|
1247 |
|
|
err("double unregister");
|
1248 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1249 |
|
|
return;
|
1250 |
|
|
}
|
1251 |
|
|
|
1252 |
|
|
ts->is_registered = 0;
|
1253 |
|
|
wake_up_interruptible(&ts->wait); /* So we see its gone */
|
1254 |
|
|
|
1255 |
|
|
/* restore default digitiser values */
|
1256 |
|
|
dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6);
|
1257 |
|
|
if (ts->is_wm9712)
|
1258 |
|
|
dig2 = WM9712_RPU(1);
|
1259 |
|
|
else
|
1260 |
|
|
dig2 = 0x0;
|
1261 |
|
|
|
1262 |
|
|
codec->codec_write(codec, AC97_WM97XX_DIGITISER1, dig1);
|
1263 |
|
|
codec->codec_write(codec, AC97_WM97XX_DIGITISER2, dig2);
|
1264 |
|
|
ts->codec = NULL;
|
1265 |
|
|
|
1266 |
|
|
spin_unlock_irqrestore(&ts->lock, flags);
|
1267 |
|
|
}
|
1268 |
|
|
|
1269 |
|
|
static struct miscdevice wm97xx_misc = {
|
1270 |
|
|
minor: TS_MINOR,
|
1271 |
|
|
name: "touchscreen/wm97xx",
|
1272 |
|
|
fops: &ts_fops,
|
1273 |
|
|
};
|
1274 |
|
|
|
1275 |
|
|
static int __init wm97xx_ts_init_module(void)
|
1276 |
|
|
{
|
1277 |
|
|
wm97xx_ts_t* ts = &wm97xx_ts;
|
1278 |
|
|
int ret;
|
1279 |
|
|
char proc_str[64];
|
1280 |
|
|
|
1281 |
|
|
info("Wolfson WM9705/WM9712 Touchscreen Controller");
|
1282 |
|
|
info("Version %s liam.girdwood@wolfsonmicro.com", WM_TS_VERSION);
|
1283 |
|
|
|
1284 |
|
|
memset(ts, 0, sizeof(wm97xx_ts_t));
|
1285 |
|
|
|
1286 |
|
|
/* register our misc device */
|
1287 |
|
|
if ((ret = misc_register(&wm97xx_misc)) < 0) {
|
1288 |
|
|
err("can't register misc device");
|
1289 |
|
|
return ret;
|
1290 |
|
|
}
|
1291 |
|
|
|
1292 |
|
|
init_waitqueue_head(&ts->wait);
|
1293 |
|
|
spin_lock_init(&ts->lock);
|
1294 |
|
|
|
1295 |
|
|
// initial calibration values
|
1296 |
|
|
ts->cal.xscale = 256;
|
1297 |
|
|
ts->cal.xtrans = 0;
|
1298 |
|
|
ts->cal.yscale = 256;
|
1299 |
|
|
ts->cal.ytrans = 0;
|
1300 |
|
|
|
1301 |
|
|
/* reset error counters */
|
1302 |
|
|
ts->overruns = 0;
|
1303 |
|
|
ts->adc_errs = 0;
|
1304 |
|
|
|
1305 |
|
|
/* register with the AC97 layer */
|
1306 |
|
|
ac97_register_driver(&wm9705_driver);
|
1307 |
|
|
ac97_register_driver(&wm9712_driver);
|
1308 |
|
|
|
1309 |
|
|
#ifdef CONFIG_PROC_FS
|
1310 |
|
|
/* register proc interface */
|
1311 |
|
|
sprintf(proc_str, "driver/%s", TS_NAME);
|
1312 |
|
|
if ((ts->wm97xx_ts_ps = create_proc_read_entry (proc_str, 0, NULL,
|
1313 |
|
|
wm97xx_read_proc, ts)) == 0)
|
1314 |
|
|
err("could not register proc interface /proc/%s", proc_str);
|
1315 |
|
|
#ifdef WM97XX_TS_DEBUG
|
1316 |
|
|
if ((ts->wm97xx_debug_ts_ps = create_proc_read_entry ("driver/ac97_registers",
|
1317 |
|
|
0, NULL,wm_debug_read_proc, ts)) == 0)
|
1318 |
|
|
err("could not register proc interface /proc/driver/ac97_registers");
|
1319 |
|
|
#endif
|
1320 |
|
|
#endif
|
1321 |
|
|
#ifdef CONFIG_PM
|
1322 |
|
|
if ((ts->pm = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, wm97xx_pm_event)) == 0)
|
1323 |
|
|
err("could not register with power management");
|
1324 |
|
|
#endif
|
1325 |
|
|
return 0;
|
1326 |
|
|
}
|
1327 |
|
|
|
1328 |
|
|
static void wm97xx_ts_cleanup_module(void)
|
1329 |
|
|
{
|
1330 |
|
|
wm97xx_ts_t* ts = &wm97xx_ts;
|
1331 |
|
|
|
1332 |
|
|
#ifdef CONFIG_PM
|
1333 |
|
|
pm_unregister (ts->pm);
|
1334 |
|
|
#endif
|
1335 |
|
|
ac97_unregister_driver(&wm9705_driver);
|
1336 |
|
|
ac97_unregister_driver(&wm9712_driver);
|
1337 |
|
|
misc_deregister(&wm97xx_misc);
|
1338 |
|
|
}
|
1339 |
|
|
|
1340 |
|
|
/* Module information */
|
1341 |
|
|
MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
|
1342 |
|
|
MODULE_DESCRIPTION("WM9705/WM9712 Touch Screen / BMON Driver");
|
1343 |
|
|
MODULE_LICENSE("GPL");
|
1344 |
|
|
|
1345 |
|
|
module_init(wm97xx_ts_init_module);
|
1346 |
|
|
module_exit(wm97xx_ts_cleanup_module);
|
1347 |
|
|
|
1348 |
|
|
#ifndef MODULE
|
1349 |
|
|
|
1350 |
|
|
static int __init wm97xx_ts_setup(char *options)
|
1351 |
|
|
{
|
1352 |
|
|
char *this_opt = options;
|
1353 |
|
|
|
1354 |
|
|
if (!options || !*options)
|
1355 |
|
|
return 0;
|
1356 |
|
|
|
1357 |
|
|
/* parse the options and check for out of range values */
|
1358 |
|
|
for(this_opt=strtok(options, ",");
|
1359 |
|
|
this_opt; this_opt=strtok(NULL, ",")) {
|
1360 |
|
|
if (!strncmp(this_opt, "pil:", 4)) {
|
1361 |
|
|
this_opt+=4;
|
1362 |
|
|
pil = simple_strtol(this_opt, NULL, 0);
|
1363 |
|
|
if (pil < 0 || pil > 2)
|
1364 |
|
|
pil = 0;
|
1365 |
|
|
continue;
|
1366 |
|
|
}
|
1367 |
|
|
if (!strncmp(this_opt, "rpu:", 4)) {
|
1368 |
|
|
this_opt+=4;
|
1369 |
|
|
rpu = simple_strtol(this_opt, NULL, 0);
|
1370 |
|
|
if (rpu < 0 || rpu > 31)
|
1371 |
|
|
rpu = 0;
|
1372 |
|
|
continue;
|
1373 |
|
|
}
|
1374 |
|
|
if (!strncmp(this_opt, "pdd:", 4)) {
|
1375 |
|
|
this_opt+=4;
|
1376 |
|
|
pdd = simple_strtol(this_opt, NULL, 0);
|
1377 |
|
|
if (pdd < 0 || pdd > 15)
|
1378 |
|
|
pdd = 0;
|
1379 |
|
|
continue;
|
1380 |
|
|
}
|
1381 |
|
|
if (!strncmp(this_opt, "delay:", 6)) {
|
1382 |
|
|
this_opt+=6;
|
1383 |
|
|
delay = simple_strtol(this_opt, NULL, 0);
|
1384 |
|
|
if (delay < 0 || delay > 15)
|
1385 |
|
|
delay = 4;
|
1386 |
|
|
continue;
|
1387 |
|
|
}
|
1388 |
|
|
if (!strncmp(this_opt, "five_wire:", 10)) {
|
1389 |
|
|
this_opt+=10;
|
1390 |
|
|
five_wire = simple_strtol(this_opt, NULL, 0);
|
1391 |
|
|
if (five_wire < 0 || five_wire > 1)
|
1392 |
|
|
five_wire = 0;
|
1393 |
|
|
continue;
|
1394 |
|
|
}
|
1395 |
|
|
if (!strncmp(this_opt, "mode:", 5)) {
|
1396 |
|
|
this_opt+=5;
|
1397 |
|
|
mode = simple_strtol(this_opt, NULL, 0);
|
1398 |
|
|
if (mode < 0 || mode > 2)
|
1399 |
|
|
mode = 0;
|
1400 |
|
|
continue;
|
1401 |
|
|
}
|
1402 |
|
|
}
|
1403 |
|
|
return 1;
|
1404 |
|
|
}
|
1405 |
|
|
|
1406 |
|
|
__setup("wm97xx_ts=", wm97xx_ts_setup);
|
1407 |
|
|
|
1408 |
|
|
#endif /* MODULE */
|