cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wm9705.c (9217B)


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