File Index
Files and directories
Sign in
FILE

wm9705.c

File preview with syntax highlighting.

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