18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci * Author: Liam Girdwood <lrg@slimlogic.co.uk> 78c2ecf20Sopenharmony_ci * Parts Copyright : Ian Molton <spyro@f2s.com> 88c2ecf20Sopenharmony_ci * Andrew Zabolotny <zap@homelink.ru> 98c2ecf20Sopenharmony_ci * Russell King <rmk@arm.linux.org.uk> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/input.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/bitops.h> 188c2ecf20Sopenharmony_ci#include <linux/wm97xx.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define TS_NAME "wm97xx" 218c2ecf20Sopenharmony_ci#define WM9712_VERSION "1.00" 228c2ecf20Sopenharmony_ci#define DEFAULT_PRESSURE 0xb0c0 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Module parameters 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Set internal pull up for pen detect. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) 328c2ecf20Sopenharmony_ci * i.e. pull up resistance = 64k Ohms / rpu. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Adjust this value if you are having problems with pen detect not 358c2ecf20Sopenharmony_ci * detecting any down event. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic int rpu = 8; 388c2ecf20Sopenharmony_cimodule_param(rpu, int, 0); 398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect."); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * Set current used for pressure measurement. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Set pil = 2 to use 400uA 458c2ecf20Sopenharmony_ci * pil = 1 to use 200uA and 468c2ecf20Sopenharmony_ci * pil = 0 to disable pressure measurement. 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * This is used to increase the range of values returned by the adc 498c2ecf20Sopenharmony_ci * when measureing touchpanel pressure. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistatic int pil; 528c2ecf20Sopenharmony_cimodule_param(pil, int, 0); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pil, "Set current used for pressure measurement."); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * Set threshold for pressure measurement. 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * Pen down pressure below threshold is ignored. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic int pressure = DEFAULT_PRESSURE & 0xfff; 618c2ecf20Sopenharmony_cimodule_param(pressure, int, 0); 628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Set adc sample delay. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * For accurate touchpanel measurements, some settling time may be 688c2ecf20Sopenharmony_ci * required between the switch matrix applying a voltage across the 698c2ecf20Sopenharmony_ci * touchpanel plate and the ADC sampling the signal. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * This delay can be set by setting delay = n, where n is the array 728c2ecf20Sopenharmony_ci * position of the delay in the array delay_table below. 738c2ecf20Sopenharmony_ci * Long delays > 1ms are supported for completeness, but are not 748c2ecf20Sopenharmony_ci * recommended. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistatic int delay = 3; 778c2ecf20Sopenharmony_cimodule_param(delay, int, 0); 788c2ecf20Sopenharmony_ciMODULE_PARM_DESC(delay, "Set adc sample delay."); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * Set five_wire = 1 to use a 5 wire touchscreen. 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * NOTE: Five wire mode does not allow for readback of pressure. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic int five_wire; 868c2ecf20Sopenharmony_cimodule_param(five_wire, int, 0); 878c2ecf20Sopenharmony_ciMODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * Set adc mask function. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Sources of glitch noise, such as signals driving an LCD display, may feed 938c2ecf20Sopenharmony_ci * through to the touch screen plates and affect measurement accuracy. In 948c2ecf20Sopenharmony_ci * order to minimise this, a signal may be applied to the MASK pin to delay or 958c2ecf20Sopenharmony_ci * synchronise the sampling. 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * 0 = No delay or sync 988c2ecf20Sopenharmony_ci * 1 = High on pin stops conversions 998c2ecf20Sopenharmony_ci * 2 = Edge triggered, edge on pin delays conversion by delay param (above) 1008c2ecf20Sopenharmony_ci * 3 = Edge triggered, edge on pin starts conversion after delay param 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic int mask; 1038c2ecf20Sopenharmony_cimodule_param(mask, int, 0); 1048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mask, "Set adc mask function."); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * Coordinate Polling Enable. 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together 1108c2ecf20Sopenharmony_ci * for every poll. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_cistatic int coord; 1138c2ecf20Sopenharmony_cimodule_param(coord, int, 0); 1148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(coord, "Polling coordinate mode"); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * ADC sample delay times in uS 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic const int delay_table[] = { 1208c2ecf20Sopenharmony_ci 21, /* 1 AC97 Link frames */ 1218c2ecf20Sopenharmony_ci 42, /* 2 */ 1228c2ecf20Sopenharmony_ci 84, /* 4 */ 1238c2ecf20Sopenharmony_ci 167, /* 8 */ 1248c2ecf20Sopenharmony_ci 333, /* 16 */ 1258c2ecf20Sopenharmony_ci 667, /* 32 */ 1268c2ecf20Sopenharmony_ci 1000, /* 48 */ 1278c2ecf20Sopenharmony_ci 1333, /* 64 */ 1288c2ecf20Sopenharmony_ci 2000, /* 96 */ 1298c2ecf20Sopenharmony_ci 2667, /* 128 */ 1308c2ecf20Sopenharmony_ci 3333, /* 160 */ 1318c2ecf20Sopenharmony_ci 4000, /* 192 */ 1328c2ecf20Sopenharmony_ci 4667, /* 224 */ 1338c2ecf20Sopenharmony_ci 5333, /* 256 */ 1348c2ecf20Sopenharmony_ci 6000, /* 288 */ 1358c2ecf20Sopenharmony_ci 0 /* No delay, switch matrix always on */ 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* 1398c2ecf20Sopenharmony_ci * Delay after issuing a POLL command. 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * The delay is 3 AC97 link frames + the touchpanel settling delay 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistatic inline void poll_delay(int d) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci udelay(3 * AC97_LINK_FRAME + delay_table[d]); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * set up the physical settings of the WM9712 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic void wm9712_phy_init(struct wm97xx *wm) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci u16 dig1 = 0; 1548c2ecf20Sopenharmony_ci u16 dig2 = WM97XX_RPR | WM9712_RPU(1); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* WM9712 rpu */ 1578c2ecf20Sopenharmony_ci if (rpu) { 1588c2ecf20Sopenharmony_ci dig2 &= 0xffc0; 1598c2ecf20Sopenharmony_ci dig2 |= WM9712_RPU(rpu); 1608c2ecf20Sopenharmony_ci dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms\n", 1618c2ecf20Sopenharmony_ci 64000 / rpu); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* WM9712 five wire */ 1658c2ecf20Sopenharmony_ci if (five_wire) { 1668c2ecf20Sopenharmony_ci dig2 |= WM9712_45W; 1678c2ecf20Sopenharmony_ci dev_dbg(wm->dev, "setting 5-wire touchscreen mode.\n"); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (pil) { 1708c2ecf20Sopenharmony_ci dev_warn(wm->dev, "pressure measurement is not " 1718c2ecf20Sopenharmony_ci "supported in 5-wire mode\n"); 1728c2ecf20Sopenharmony_ci pil = 0; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* touchpanel pressure current*/ 1778c2ecf20Sopenharmony_ci if (pil == 2) { 1788c2ecf20Sopenharmony_ci dig2 |= WM9712_PIL; 1798c2ecf20Sopenharmony_ci dev_dbg(wm->dev, 1808c2ecf20Sopenharmony_ci "setting pressure measurement current to 400uA.\n"); 1818c2ecf20Sopenharmony_ci } else if (pil) 1828c2ecf20Sopenharmony_ci dev_dbg(wm->dev, 1838c2ecf20Sopenharmony_ci "setting pressure measurement current to 200uA.\n"); 1848c2ecf20Sopenharmony_ci if (!pil) 1858c2ecf20Sopenharmony_ci pressure = 0; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* polling mode sample settling delay */ 1888c2ecf20Sopenharmony_ci if (delay < 0 || delay > 15) { 1898c2ecf20Sopenharmony_ci dev_dbg(wm->dev, "supplied delay out of range.\n"); 1908c2ecf20Sopenharmony_ci delay = 4; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci dig1 &= 0xff0f; 1938c2ecf20Sopenharmony_ci dig1 |= WM97XX_DELAY(delay); 1948c2ecf20Sopenharmony_ci dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.\n", 1958c2ecf20Sopenharmony_ci delay_table[delay]); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* mask */ 1988c2ecf20Sopenharmony_ci dig2 |= ((mask & 0x3) << 6); 1998c2ecf20Sopenharmony_ci if (mask) { 2008c2ecf20Sopenharmony_ci u16 reg; 2018c2ecf20Sopenharmony_ci /* Set GPIO4 as Mask Pin*/ 2028c2ecf20Sopenharmony_ci reg = wm97xx_reg_read(wm, AC97_MISC_AFE); 2038c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); 2048c2ecf20Sopenharmony_ci reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); 2058c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* wait - coord mode */ 2098c2ecf20Sopenharmony_ci if (coord) 2108c2ecf20Sopenharmony_ci dig2 |= WM9712_WAIT; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); 2138c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void wm9712_dig_enable(struct wm97xx *wm, int enable) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u16 dig2 = wm->dig[2]; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (enable) { 2218c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, 2228c2ecf20Sopenharmony_ci dig2 | WM97XX_PRP_DET_DIG); 2238c2ecf20Sopenharmony_ci wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ 2248c2ecf20Sopenharmony_ci } else 2258c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, 2268c2ecf20Sopenharmony_ci dig2 & ~WM97XX_PRP_DET_DIG); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void wm9712_aux_prepare(struct wm97xx *wm) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); 2328c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); 2338c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void wm9712_dig_restore(struct wm97xx *wm) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); 2398c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic inline int is_pden(struct wm97xx *wm) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci return wm->dig[2] & WM9712_PDEN; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * Read a sample from the WM9712 adc in polling mode. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int timeout = 5 * delay; 2538c2ecf20Sopenharmony_ci bool wants_pen = adcsel & WM97XX_PEN_DOWN; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (wants_pen && !wm->pen_probably_down) { 2568c2ecf20Sopenharmony_ci u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); 2578c2ecf20Sopenharmony_ci if (!(data & WM97XX_PEN_DOWN)) 2588c2ecf20Sopenharmony_ci return RC_PENUP; 2598c2ecf20Sopenharmony_ci wm->pen_probably_down = 1; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* set up digitiser */ 2638c2ecf20Sopenharmony_ci if (wm->mach_ops && wm->mach_ops->pre_sample) 2648c2ecf20Sopenharmony_ci wm->mach_ops->pre_sample(adcsel); 2658c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK) 2668c2ecf20Sopenharmony_ci | WM97XX_POLL | WM97XX_DELAY(delay)); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* wait 3 AC97 time slots + delay for conversion */ 2698c2ecf20Sopenharmony_ci poll_delay(delay); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* wait for POLL to go low */ 2728c2ecf20Sopenharmony_ci while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) 2738c2ecf20Sopenharmony_ci && timeout) { 2748c2ecf20Sopenharmony_ci udelay(AC97_LINK_FRAME); 2758c2ecf20Sopenharmony_ci timeout--; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (timeout <= 0) { 2798c2ecf20Sopenharmony_ci /* If PDEN is set, we can get a timeout when pen goes up */ 2808c2ecf20Sopenharmony_ci if (is_pden(wm)) 2818c2ecf20Sopenharmony_ci wm->pen_probably_down = 0; 2828c2ecf20Sopenharmony_ci else 2838c2ecf20Sopenharmony_ci dev_dbg(wm->dev, "adc sample timeout\n"); 2848c2ecf20Sopenharmony_ci return RC_PENUP; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); 2888c2ecf20Sopenharmony_ci if (wm->mach_ops && wm->mach_ops->post_sample) 2898c2ecf20Sopenharmony_ci wm->mach_ops->post_sample(adcsel); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* check we have correct sample */ 2928c2ecf20Sopenharmony_ci if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { 2938c2ecf20Sopenharmony_ci dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x\n", 2948c2ecf20Sopenharmony_ci adcsel & WM97XX_ADCSEL_MASK, 2958c2ecf20Sopenharmony_ci *sample & WM97XX_ADCSEL_MASK); 2968c2ecf20Sopenharmony_ci return RC_AGAIN; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { 3008c2ecf20Sopenharmony_ci /* Sometimes it reads a wrong value the first time. */ 3018c2ecf20Sopenharmony_ci *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); 3028c2ecf20Sopenharmony_ci if (!(*sample & WM97XX_PEN_DOWN)) { 3038c2ecf20Sopenharmony_ci wm->pen_probably_down = 0; 3048c2ecf20Sopenharmony_ci return RC_PENUP; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return RC_VALID; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* 3128c2ecf20Sopenharmony_ci * Read a coord from the WM9712 adc in polling mode. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_cistatic int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci int timeout = 5 * delay; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!wm->pen_probably_down) { 3198c2ecf20Sopenharmony_ci u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); 3208c2ecf20Sopenharmony_ci if (!(data_rd & WM97XX_PEN_DOWN)) 3218c2ecf20Sopenharmony_ci return RC_PENUP; 3228c2ecf20Sopenharmony_ci wm->pen_probably_down = 1; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* set up digitiser */ 3268c2ecf20Sopenharmony_ci if (wm->mach_ops && wm->mach_ops->pre_sample) 3278c2ecf20Sopenharmony_ci wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 3308c2ecf20Sopenharmony_ci WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* wait 3 AC97 time slots + delay for conversion and read x */ 3338c2ecf20Sopenharmony_ci poll_delay(delay); 3348c2ecf20Sopenharmony_ci data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); 3358c2ecf20Sopenharmony_ci /* wait for POLL to go low */ 3368c2ecf20Sopenharmony_ci while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) 3378c2ecf20Sopenharmony_ci && timeout) { 3388c2ecf20Sopenharmony_ci udelay(AC97_LINK_FRAME); 3398c2ecf20Sopenharmony_ci timeout--; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (timeout <= 0) { 3438c2ecf20Sopenharmony_ci /* If PDEN is set, we can get a timeout when pen goes up */ 3448c2ecf20Sopenharmony_ci if (is_pden(wm)) 3458c2ecf20Sopenharmony_ci wm->pen_probably_down = 0; 3468c2ecf20Sopenharmony_ci else 3478c2ecf20Sopenharmony_ci dev_dbg(wm->dev, "adc sample timeout\n"); 3488c2ecf20Sopenharmony_ci return RC_PENUP; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* read back y data */ 3528c2ecf20Sopenharmony_ci data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); 3538c2ecf20Sopenharmony_ci if (pil) 3548c2ecf20Sopenharmony_ci data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); 3558c2ecf20Sopenharmony_ci else 3568c2ecf20Sopenharmony_ci data->p = DEFAULT_PRESSURE; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (wm->mach_ops && wm->mach_ops->post_sample) 3598c2ecf20Sopenharmony_ci wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* check we have correct sample */ 3628c2ecf20Sopenharmony_ci if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) 3638c2ecf20Sopenharmony_ci goto err; 3648c2ecf20Sopenharmony_ci if (pil && !(data->p & WM97XX_ADCSEL_PRES)) 3658c2ecf20Sopenharmony_ci goto err; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) { 3688c2ecf20Sopenharmony_ci wm->pen_probably_down = 0; 3698c2ecf20Sopenharmony_ci return RC_PENUP; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci return RC_VALID; 3728c2ecf20Sopenharmony_cierr: 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* 3778c2ecf20Sopenharmony_ci * Sample the WM9712 touchscreen in polling mode 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_cistatic int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci int rc; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (coord) { 3848c2ecf20Sopenharmony_ci rc = wm9712_poll_coord(wm, data); 3858c2ecf20Sopenharmony_ci if (rc != RC_VALID) 3868c2ecf20Sopenharmony_ci return rc; 3878c2ecf20Sopenharmony_ci } else { 3888c2ecf20Sopenharmony_ci rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, 3898c2ecf20Sopenharmony_ci &data->x); 3908c2ecf20Sopenharmony_ci if (rc != RC_VALID) 3918c2ecf20Sopenharmony_ci return rc; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, 3948c2ecf20Sopenharmony_ci &data->y); 3958c2ecf20Sopenharmony_ci if (rc != RC_VALID) 3968c2ecf20Sopenharmony_ci return rc; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (pil && !five_wire) { 3998c2ecf20Sopenharmony_ci rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, 4008c2ecf20Sopenharmony_ci &data->p); 4018c2ecf20Sopenharmony_ci if (rc != RC_VALID) 4028c2ecf20Sopenharmony_ci return rc; 4038c2ecf20Sopenharmony_ci } else 4048c2ecf20Sopenharmony_ci data->p = DEFAULT_PRESSURE; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci return RC_VALID; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* 4108c2ecf20Sopenharmony_ci * Enable WM9712 continuous mode, i.e. touch data is streamed across 4118c2ecf20Sopenharmony_ci * an AC97 slot 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_cistatic int wm9712_acc_enable(struct wm97xx *wm, int enable) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci u16 dig1, dig2; 4168c2ecf20Sopenharmony_ci int ret = 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci dig1 = wm->dig[1]; 4198c2ecf20Sopenharmony_ci dig2 = wm->dig[2]; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (enable) { 4228c2ecf20Sopenharmony_ci /* continuous mode */ 4238c2ecf20Sopenharmony_ci if (wm->mach_ops->acc_startup) { 4248c2ecf20Sopenharmony_ci ret = wm->mach_ops->acc_startup(wm); 4258c2ecf20Sopenharmony_ci if (ret < 0) 4268c2ecf20Sopenharmony_ci return ret; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | 4298c2ecf20Sopenharmony_ci WM97XX_DELAY_MASK | WM97XX_SLT_MASK); 4308c2ecf20Sopenharmony_ci dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | 4318c2ecf20Sopenharmony_ci WM97XX_DELAY(delay) | 4328c2ecf20Sopenharmony_ci WM97XX_SLT(wm->acc_slot) | 4338c2ecf20Sopenharmony_ci WM97XX_RATE(wm->acc_rate); 4348c2ecf20Sopenharmony_ci if (pil) 4358c2ecf20Sopenharmony_ci dig1 |= WM97XX_ADCSEL_PRES; 4368c2ecf20Sopenharmony_ci dig2 |= WM9712_PDEN; 4378c2ecf20Sopenharmony_ci } else { 4388c2ecf20Sopenharmony_ci dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); 4398c2ecf20Sopenharmony_ci dig2 &= ~WM9712_PDEN; 4408c2ecf20Sopenharmony_ci if (wm->mach_ops->acc_shutdown) 4418c2ecf20Sopenharmony_ci wm->mach_ops->acc_shutdown(wm); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); 4458c2ecf20Sopenharmony_ci wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistruct wm97xx_codec_drv wm9712_codec = { 4518c2ecf20Sopenharmony_ci .id = WM9712_ID2, 4528c2ecf20Sopenharmony_ci .name = "wm9712", 4538c2ecf20Sopenharmony_ci .poll_sample = wm9712_poll_sample, 4548c2ecf20Sopenharmony_ci .poll_touch = wm9712_poll_touch, 4558c2ecf20Sopenharmony_ci .acc_enable = wm9712_acc_enable, 4568c2ecf20Sopenharmony_ci .phy_init = wm9712_phy_init, 4578c2ecf20Sopenharmony_ci .dig_enable = wm9712_dig_enable, 4588c2ecf20Sopenharmony_ci .dig_restore = wm9712_dig_restore, 4598c2ecf20Sopenharmony_ci .aux_prepare = wm9712_aux_prepare, 4608c2ecf20Sopenharmony_ci}; 4618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm9712_codec); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci/* Module information */ 4648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); 4658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("WM9712 Touch Screen Driver"); 4668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 467