1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
6*4882a593Smuzhiyun * Author: Liam Girdwood <lrg@slimlogic.co.uk>
7*4882a593Smuzhiyun * Parts Copyright : Ian Molton <spyro@f2s.com>
8*4882a593Smuzhiyun * Andrew Zabolotny <zap@homelink.ru>
9*4882a593Smuzhiyun * Russell King <rmk@arm.linux.org.uk>
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/moduleparam.h>
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/input.h>
16*4882a593Smuzhiyun #include <linux/delay.h>
17*4882a593Smuzhiyun #include <linux/bitops.h>
18*4882a593Smuzhiyun #include <linux/wm97xx.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define TS_NAME "wm97xx"
21*4882a593Smuzhiyun #define WM9705_VERSION "1.00"
22*4882a593Smuzhiyun #define DEFAULT_PRESSURE 0xb0c0
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /*
25*4882a593Smuzhiyun * Module parameters
26*4882a593Smuzhiyun */
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun * Set current used for pressure measurement.
30*4882a593Smuzhiyun *
31*4882a593Smuzhiyun * Set pil = 2 to use 400uA
32*4882a593Smuzhiyun * pil = 1 to use 200uA and
33*4882a593Smuzhiyun * pil = 0 to disable pressure measurement.
34*4882a593Smuzhiyun *
35*4882a593Smuzhiyun * This is used to increase the range of values returned by the adc
36*4882a593Smuzhiyun * when measureing touchpanel pressure.
37*4882a593Smuzhiyun */
38*4882a593Smuzhiyun static int pil;
39*4882a593Smuzhiyun module_param(pil, int, 0);
40*4882a593Smuzhiyun MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun /*
43*4882a593Smuzhiyun * Set threshold for pressure measurement.
44*4882a593Smuzhiyun *
45*4882a593Smuzhiyun * Pen down pressure below threshold is ignored.
46*4882a593Smuzhiyun */
47*4882a593Smuzhiyun static int pressure = DEFAULT_PRESSURE & 0xfff;
48*4882a593Smuzhiyun module_param(pressure, int, 0);
49*4882a593Smuzhiyun MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /*
52*4882a593Smuzhiyun * Set adc sample delay.
53*4882a593Smuzhiyun *
54*4882a593Smuzhiyun * For accurate touchpanel measurements, some settling time may be
55*4882a593Smuzhiyun * required between the switch matrix applying a voltage across the
56*4882a593Smuzhiyun * touchpanel plate and the ADC sampling the signal.
57*4882a593Smuzhiyun *
58*4882a593Smuzhiyun * This delay can be set by setting delay = n, where n is the array
59*4882a593Smuzhiyun * position of the delay in the array delay_table below.
60*4882a593Smuzhiyun * Long delays > 1ms are supported for completeness, but are not
61*4882a593Smuzhiyun * recommended.
62*4882a593Smuzhiyun */
63*4882a593Smuzhiyun static int delay = 4;
64*4882a593Smuzhiyun module_param(delay, int, 0);
65*4882a593Smuzhiyun MODULE_PARM_DESC(delay, "Set adc sample delay.");
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun /*
68*4882a593Smuzhiyun * Pen detect comparator threshold.
69*4882a593Smuzhiyun *
70*4882a593Smuzhiyun * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
71*4882a593Smuzhiyun * i.e. 1 = Vmid/15 threshold
72*4882a593Smuzhiyun * 15 = Vmid/1 threshold
73*4882a593Smuzhiyun *
74*4882a593Smuzhiyun * Adjust this value if you are having problems with pen detect not
75*4882a593Smuzhiyun * detecting any down events.
76*4882a593Smuzhiyun */
77*4882a593Smuzhiyun static int pdd = 8;
78*4882a593Smuzhiyun module_param(pdd, int, 0);
79*4882a593Smuzhiyun MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /*
82*4882a593Smuzhiyun * Set adc mask function.
83*4882a593Smuzhiyun *
84*4882a593Smuzhiyun * Sources of glitch noise, such as signals driving an LCD display, may feed
85*4882a593Smuzhiyun * through to the touch screen plates and affect measurement accuracy. In
86*4882a593Smuzhiyun * order to minimise this, a signal may be applied to the MASK pin to delay or
87*4882a593Smuzhiyun * synchronise the sampling.
88*4882a593Smuzhiyun *
89*4882a593Smuzhiyun * 0 = No delay or sync
90*4882a593Smuzhiyun * 1 = High on pin stops conversions
91*4882a593Smuzhiyun * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
92*4882a593Smuzhiyun * 3 = Edge triggered, edge on pin starts conversion after delay param
93*4882a593Smuzhiyun */
94*4882a593Smuzhiyun static int mask;
95*4882a593Smuzhiyun module_param(mask, int, 0);
96*4882a593Smuzhiyun MODULE_PARM_DESC(mask, "Set adc mask function.");
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun /*
99*4882a593Smuzhiyun * ADC sample delay times in uS
100*4882a593Smuzhiyun */
101*4882a593Smuzhiyun static const int delay_table[] = {
102*4882a593Smuzhiyun 21, /* 1 AC97 Link frames */
103*4882a593Smuzhiyun 42, /* 2 */
104*4882a593Smuzhiyun 84, /* 4 */
105*4882a593Smuzhiyun 167, /* 8 */
106*4882a593Smuzhiyun 333, /* 16 */
107*4882a593Smuzhiyun 667, /* 32 */
108*4882a593Smuzhiyun 1000, /* 48 */
109*4882a593Smuzhiyun 1333, /* 64 */
110*4882a593Smuzhiyun 2000, /* 96 */
111*4882a593Smuzhiyun 2667, /* 128 */
112*4882a593Smuzhiyun 3333, /* 160 */
113*4882a593Smuzhiyun 4000, /* 192 */
114*4882a593Smuzhiyun 4667, /* 224 */
115*4882a593Smuzhiyun 5333, /* 256 */
116*4882a593Smuzhiyun 6000, /* 288 */
117*4882a593Smuzhiyun 0 /* No delay, switch matrix always on */
118*4882a593Smuzhiyun };
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun /*
121*4882a593Smuzhiyun * Delay after issuing a POLL command.
122*4882a593Smuzhiyun *
123*4882a593Smuzhiyun * The delay is 3 AC97 link frames + the touchpanel settling delay
124*4882a593Smuzhiyun */
poll_delay(int d)125*4882a593Smuzhiyun static inline void poll_delay(int d)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun udelay(3 * AC97_LINK_FRAME + delay_table[d]);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /*
131*4882a593Smuzhiyun * set up the physical settings of the WM9705
132*4882a593Smuzhiyun */
wm9705_phy_init(struct wm97xx * wm)133*4882a593Smuzhiyun static void wm9705_phy_init(struct wm97xx *wm)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun u16 dig1 = 0, dig2 = WM97XX_RPR;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun /*
138*4882a593Smuzhiyun * mute VIDEO and AUX as they share X and Y touchscreen
139*4882a593Smuzhiyun * inputs on the WM9705
140*4882a593Smuzhiyun */
141*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_AUX, 0x8000);
142*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* touchpanel pressure current*/
145*4882a593Smuzhiyun if (pil == 2) {
146*4882a593Smuzhiyun dig2 |= WM9705_PIL;
147*4882a593Smuzhiyun dev_dbg(wm->dev,
148*4882a593Smuzhiyun "setting pressure measurement current to 400uA.");
149*4882a593Smuzhiyun } else if (pil)
150*4882a593Smuzhiyun dev_dbg(wm->dev,
151*4882a593Smuzhiyun "setting pressure measurement current to 200uA.");
152*4882a593Smuzhiyun if (!pil)
153*4882a593Smuzhiyun pressure = 0;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun /* polling mode sample settling delay */
156*4882a593Smuzhiyun if (delay != 4) {
157*4882a593Smuzhiyun if (delay < 0 || delay > 15) {
158*4882a593Smuzhiyun dev_dbg(wm->dev, "supplied delay out of range.");
159*4882a593Smuzhiyun delay = 4;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun dig1 &= 0xff0f;
163*4882a593Smuzhiyun dig1 |= WM97XX_DELAY(delay);
164*4882a593Smuzhiyun dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
165*4882a593Smuzhiyun delay_table[delay]);
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun /* WM9705 pdd */
168*4882a593Smuzhiyun dig2 |= (pdd & 0x000f);
169*4882a593Smuzhiyun dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun /* mask */
172*4882a593Smuzhiyun dig2 |= ((mask & 0x3) << 4);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
175*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
wm9705_dig_enable(struct wm97xx * wm,int enable)178*4882a593Smuzhiyun static void wm9705_dig_enable(struct wm97xx *wm, int enable)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun if (enable) {
181*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
182*4882a593Smuzhiyun wm->dig[2] | WM97XX_PRP_DET_DIG);
183*4882a593Smuzhiyun wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
184*4882a593Smuzhiyun } else
185*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
186*4882a593Smuzhiyun wm->dig[2] & ~WM97XX_PRP_DET_DIG);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
wm9705_aux_prepare(struct wm97xx * wm)189*4882a593Smuzhiyun static void wm9705_aux_prepare(struct wm97xx *wm)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
192*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
193*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
wm9705_dig_restore(struct wm97xx * wm)196*4882a593Smuzhiyun static void wm9705_dig_restore(struct wm97xx *wm)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
199*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
is_pden(struct wm97xx * wm)202*4882a593Smuzhiyun static inline int is_pden(struct wm97xx *wm)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun return wm->dig[2] & WM9705_PDEN;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun /*
208*4882a593Smuzhiyun * Read a sample from the WM9705 adc in polling mode.
209*4882a593Smuzhiyun */
wm9705_poll_sample(struct wm97xx * wm,int adcsel,int * sample)210*4882a593Smuzhiyun static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun int timeout = 5 * delay;
213*4882a593Smuzhiyun bool wants_pen = adcsel & WM97XX_PEN_DOWN;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (wants_pen && !wm->pen_probably_down) {
216*4882a593Smuzhiyun u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
217*4882a593Smuzhiyun if (!(data & WM97XX_PEN_DOWN))
218*4882a593Smuzhiyun return RC_PENUP;
219*4882a593Smuzhiyun wm->pen_probably_down = 1;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun /* set up digitiser */
223*4882a593Smuzhiyun if (wm->mach_ops && wm->mach_ops->pre_sample)
224*4882a593Smuzhiyun wm->mach_ops->pre_sample(adcsel);
225*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
226*4882a593Smuzhiyun | WM97XX_POLL | WM97XX_DELAY(delay));
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun /* wait 3 AC97 time slots + delay for conversion */
229*4882a593Smuzhiyun poll_delay(delay);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun /* wait for POLL to go low */
232*4882a593Smuzhiyun while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
233*4882a593Smuzhiyun && timeout) {
234*4882a593Smuzhiyun udelay(AC97_LINK_FRAME);
235*4882a593Smuzhiyun timeout--;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun if (timeout == 0) {
239*4882a593Smuzhiyun /* If PDEN is set, we can get a timeout when pen goes up */
240*4882a593Smuzhiyun if (is_pden(wm))
241*4882a593Smuzhiyun wm->pen_probably_down = 0;
242*4882a593Smuzhiyun else
243*4882a593Smuzhiyun dev_dbg(wm->dev, "adc sample timeout");
244*4882a593Smuzhiyun return RC_PENUP;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
248*4882a593Smuzhiyun if (wm->mach_ops && wm->mach_ops->post_sample)
249*4882a593Smuzhiyun wm->mach_ops->post_sample(adcsel);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun /* check we have correct sample */
252*4882a593Smuzhiyun if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
253*4882a593Smuzhiyun dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
254*4882a593Smuzhiyun adcsel & WM97XX_ADCSEL_MASK,
255*4882a593Smuzhiyun *sample & WM97XX_ADCSEL_MASK);
256*4882a593Smuzhiyun return RC_PENUP;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
260*4882a593Smuzhiyun wm->pen_probably_down = 0;
261*4882a593Smuzhiyun return RC_PENUP;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun return RC_VALID;
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun /*
268*4882a593Smuzhiyun * Sample the WM9705 touchscreen in polling mode
269*4882a593Smuzhiyun */
wm9705_poll_touch(struct wm97xx * wm,struct wm97xx_data * data)270*4882a593Smuzhiyun static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun int rc;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
275*4882a593Smuzhiyun if (rc != RC_VALID)
276*4882a593Smuzhiyun return rc;
277*4882a593Smuzhiyun rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
278*4882a593Smuzhiyun if (rc != RC_VALID)
279*4882a593Smuzhiyun return rc;
280*4882a593Smuzhiyun if (pil) {
281*4882a593Smuzhiyun rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);
282*4882a593Smuzhiyun if (rc != RC_VALID)
283*4882a593Smuzhiyun return rc;
284*4882a593Smuzhiyun } else
285*4882a593Smuzhiyun data->p = DEFAULT_PRESSURE;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun return RC_VALID;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun /*
291*4882a593Smuzhiyun * Enable WM9705 continuous mode, i.e. touch data is streamed across
292*4882a593Smuzhiyun * an AC97 slot
293*4882a593Smuzhiyun */
wm9705_acc_enable(struct wm97xx * wm,int enable)294*4882a593Smuzhiyun static int wm9705_acc_enable(struct wm97xx *wm, int enable)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun u16 dig1, dig2;
297*4882a593Smuzhiyun int ret = 0;
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun dig1 = wm->dig[1];
300*4882a593Smuzhiyun dig2 = wm->dig[2];
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun if (enable) {
303*4882a593Smuzhiyun /* continuous mode */
304*4882a593Smuzhiyun if (wm->mach_ops->acc_startup &&
305*4882a593Smuzhiyun (ret = wm->mach_ops->acc_startup(wm)) < 0)
306*4882a593Smuzhiyun return ret;
307*4882a593Smuzhiyun dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
308*4882a593Smuzhiyun WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
309*4882a593Smuzhiyun dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
310*4882a593Smuzhiyun WM97XX_DELAY(delay) |
311*4882a593Smuzhiyun WM97XX_SLT(wm->acc_slot) |
312*4882a593Smuzhiyun WM97XX_RATE(wm->acc_rate);
313*4882a593Smuzhiyun if (pil)
314*4882a593Smuzhiyun dig1 |= WM97XX_ADCSEL_PRES;
315*4882a593Smuzhiyun dig2 |= WM9705_PDEN;
316*4882a593Smuzhiyun } else {
317*4882a593Smuzhiyun dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
318*4882a593Smuzhiyun dig2 &= ~WM9705_PDEN;
319*4882a593Smuzhiyun if (wm->mach_ops->acc_shutdown)
320*4882a593Smuzhiyun wm->mach_ops->acc_shutdown(wm);
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
324*4882a593Smuzhiyun wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun return ret;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun struct wm97xx_codec_drv wm9705_codec = {
330*4882a593Smuzhiyun .id = WM9705_ID2,
331*4882a593Smuzhiyun .name = "wm9705",
332*4882a593Smuzhiyun .poll_sample = wm9705_poll_sample,
333*4882a593Smuzhiyun .poll_touch = wm9705_poll_touch,
334*4882a593Smuzhiyun .acc_enable = wm9705_acc_enable,
335*4882a593Smuzhiyun .phy_init = wm9705_phy_init,
336*4882a593Smuzhiyun .dig_enable = wm9705_dig_enable,
337*4882a593Smuzhiyun .dig_restore = wm9705_dig_restore,
338*4882a593Smuzhiyun .aux_prepare = wm9705_aux_prepare,
339*4882a593Smuzhiyun };
340*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wm9705_codec);
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun /* Module information */
343*4882a593Smuzhiyun MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
344*4882a593Smuzhiyun MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
345*4882a593Smuzhiyun MODULE_LICENSE("GPL");
346