18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Philips UCB1400 touchscreen driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Nicolas Pitre 68c2ecf20Sopenharmony_ci * Created: September 25, 2006 78c2ecf20Sopenharmony_ci * Copyright: MontaVista Software, Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Spliting done by: Marek Vasut <marek.vasut@gmail.com> 108c2ecf20Sopenharmony_ci * If something doesn't work and it worked before spliting, e-mail me, 118c2ecf20Sopenharmony_ci * dont bother Nicolas please ;-) 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This code is heavily based on ucb1x00-*.c copyrighted by Russell King 148c2ecf20Sopenharmony_ci * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has 158c2ecf20Sopenharmony_ci * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/wait.h> 228c2ecf20Sopenharmony_ci#include <linux/input.h> 238c2ecf20Sopenharmony_ci#include <linux/device.h> 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/ucb1400.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define UCB1400_TS_POLL_PERIOD 10 /* ms */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic bool adcsync; 308c2ecf20Sopenharmony_cistatic int ts_delay = 55; /* us */ 318c2ecf20Sopenharmony_cistatic int ts_delay_pressure; /* us */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Switch to interrupt mode. */ 348c2ecf20Sopenharmony_cistatic void ucb1400_ts_mode_int(struct ucb1400_ts *ucb) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 378c2ecf20Sopenharmony_ci UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | 388c2ecf20Sopenharmony_ci UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | 398c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_INT); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * Switch to pressure mode, and read pressure. We don't need to wait 448c2ecf20Sopenharmony_ci * here, since both plates are being driven. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 498c2ecf20Sopenharmony_ci UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | 508c2ecf20Sopenharmony_ci UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | 518c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci udelay(ts_delay_pressure); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Switch to X position mode and measure Y plate. We switch the plate 608c2ecf20Sopenharmony_ci * configuration in pressure mode, then switch to position mode. This 618c2ecf20Sopenharmony_ci * gives a faster response time. Even so, we need to wait about 55us 628c2ecf20Sopenharmony_ci * for things to stabilise. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 678c2ecf20Sopenharmony_ci UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 688c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 698c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 708c2ecf20Sopenharmony_ci UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 718c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 728c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 738c2ecf20Sopenharmony_ci UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 748c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci udelay(ts_delay); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * Switch to Y position mode and measure X plate. We switch the plate 838c2ecf20Sopenharmony_ci * configuration in pressure mode, then switch to position mode. This 848c2ecf20Sopenharmony_ci * gives a faster response time. Even so, we need to wait about 55us 858c2ecf20Sopenharmony_ci * for things to stabilise. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cistatic int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 908c2ecf20Sopenharmony_ci UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 918c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 928c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 938c2ecf20Sopenharmony_ci UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 948c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 958c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 968c2ecf20Sopenharmony_ci UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 978c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci udelay(ts_delay); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * Switch to X plate resistance mode. Set MX to ground, PX to 1068c2ecf20Sopenharmony_ci * supply. Measure current. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 1118c2ecf20Sopenharmony_ci UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 1128c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 1138c2ecf20Sopenharmony_ci return ucb1400_adc_read(ucb->ac97, 0, adcsync); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Switch to Y plate resistance mode. Set MY to ground, PY to 1188c2ecf20Sopenharmony_ci * supply. Measure current. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 1238c2ecf20Sopenharmony_ci UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 1248c2ecf20Sopenharmony_ci UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 1258c2ecf20Sopenharmony_ci return ucb1400_adc_read(ucb->ac97, 0, adcsync); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int ucb1400_ts_pen_up(struct ucb1400_ts *ucb) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX); 1388c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); 1398c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_X, x); 1508c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_Y, y); 1518c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_PRESSURE, pressure); 1528c2ecf20Sopenharmony_ci input_report_key(idev, BTN_TOUCH, 1); 1538c2ecf20Sopenharmony_ci input_sync(idev); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void ucb1400_ts_event_release(struct input_dev *idev) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_PRESSURE, 0); 1598c2ecf20Sopenharmony_ci input_report_key(idev, BTN_TOUCH, 0); 1608c2ecf20Sopenharmony_ci input_sync(idev); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci unsigned int isr; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS); 1688c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr); 1698c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (isr & UCB_IE_TSPX) 1728c2ecf20Sopenharmony_ci ucb1400_ts_irq_disable(ucb); 1738c2ecf20Sopenharmony_ci else 1748c2ecf20Sopenharmony_ci dev_dbg(&ucb->ts_idev->dev, 1758c2ecf20Sopenharmony_ci "ucb1400: unexpected IE_STATUS = %#x\n", isr); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * A restriction with interrupts exists when using the ucb1400, as 1808c2ecf20Sopenharmony_ci * the codec read/write routines may sleep while waiting for codec 1818c2ecf20Sopenharmony_ci * access completion and uses semaphores for access control to the 1828c2ecf20Sopenharmony_ci * AC97 bus. Therefore the driver is forced to use threaded interrupt 1838c2ecf20Sopenharmony_ci * handler. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic irqreturn_t ucb1400_irq(int irqnr, void *devid) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct ucb1400_ts *ucb = devid; 1888c2ecf20Sopenharmony_ci unsigned int x, y, p; 1898c2ecf20Sopenharmony_ci bool penup; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (unlikely(irqnr != ucb->irq)) 1928c2ecf20Sopenharmony_ci return IRQ_NONE; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ucb1400_clear_pending_irq(ucb); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Start with a small delay before checking pendown state */ 1978c2ecf20Sopenharmony_ci msleep(UCB1400_TS_POLL_PERIOD); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) { 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ucb1400_adc_enable(ucb->ac97); 2028c2ecf20Sopenharmony_ci x = ucb1400_ts_read_xpos(ucb); 2038c2ecf20Sopenharmony_ci y = ucb1400_ts_read_ypos(ucb); 2048c2ecf20Sopenharmony_ci p = ucb1400_ts_read_pressure(ucb); 2058c2ecf20Sopenharmony_ci ucb1400_adc_disable(ucb->ac97); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ucb1400_ts_report_event(ucb->ts_idev, p, x, y); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci wait_event_timeout(ucb->ts_wait, ucb->stopped, 2108c2ecf20Sopenharmony_ci msecs_to_jiffies(UCB1400_TS_POLL_PERIOD)); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ucb1400_ts_event_release(ucb->ts_idev); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (!ucb->stopped) { 2168c2ecf20Sopenharmony_ci /* Switch back to interrupt mode. */ 2178c2ecf20Sopenharmony_ci ucb1400_ts_mode_int(ucb); 2188c2ecf20Sopenharmony_ci ucb1400_ts_irq_enable(ucb); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void ucb1400_ts_stop(struct ucb1400_ts *ucb) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci /* Signal IRQ thread to stop polling and disable the handler. */ 2278c2ecf20Sopenharmony_ci ucb->stopped = true; 2288c2ecf20Sopenharmony_ci mb(); 2298c2ecf20Sopenharmony_ci wake_up(&ucb->ts_wait); 2308c2ecf20Sopenharmony_ci disable_irq(ucb->irq); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ucb1400_ts_irq_disable(ucb); 2338c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* Must be called with ts->lock held */ 2378c2ecf20Sopenharmony_cistatic void ucb1400_ts_start(struct ucb1400_ts *ucb) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci /* Tell IRQ thread that it may poll the device. */ 2408c2ecf20Sopenharmony_ci ucb->stopped = false; 2418c2ecf20Sopenharmony_ci mb(); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ucb1400_ts_mode_int(ucb); 2448c2ecf20Sopenharmony_ci ucb1400_ts_irq_enable(ucb); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci enable_irq(ucb->irq); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int ucb1400_ts_open(struct input_dev *idev) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct ucb1400_ts *ucb = input_get_drvdata(idev); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ucb1400_ts_start(ucb); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void ucb1400_ts_close(struct input_dev *idev) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct ucb1400_ts *ucb = input_get_drvdata(idev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ucb1400_ts_stop(ucb); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#ifndef NO_IRQ 2668c2ecf20Sopenharmony_ci#define NO_IRQ 0 2678c2ecf20Sopenharmony_ci#endif 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * Try to probe our interrupt, rather than relying on lots of 2718c2ecf20Sopenharmony_ci * hard-coded machine dependencies. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb, 2748c2ecf20Sopenharmony_ci struct platform_device *pdev) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci unsigned long mask, timeout; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci mask = probe_irq_on(); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Enable the ADC interrupt. */ 2818c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC); 2828c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC); 2838c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); 2848c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Cause an ADC interrupt. */ 2878c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA); 2888c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* Wait for the conversion to complete. */ 2918c2ecf20Sopenharmony_ci timeout = jiffies + HZ/2; 2928c2ecf20Sopenharmony_ci while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) & 2938c2ecf20Sopenharmony_ci UCB_ADC_DAT_VALID)) { 2948c2ecf20Sopenharmony_ci cpu_relax(); 2958c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 2968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "timed out in IRQ probe\n"); 2978c2ecf20Sopenharmony_ci probe_irq_off(mask); 2988c2ecf20Sopenharmony_ci return -ENODEV; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Disable and clear interrupt. */ 3048c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0); 3058c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0); 3068c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); 3078c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Read triggered interrupt. */ 3108c2ecf20Sopenharmony_ci ucb->irq = probe_irq_off(mask); 3118c2ecf20Sopenharmony_ci if (ucb->irq < 0 || ucb->irq == NO_IRQ) 3128c2ecf20Sopenharmony_ci return -ENODEV; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int ucb1400_ts_probe(struct platform_device *pdev) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev); 3208c2ecf20Sopenharmony_ci int error, x_res, y_res; 3218c2ecf20Sopenharmony_ci u16 fcsr; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ucb->ts_idev = input_allocate_device(); 3248c2ecf20Sopenharmony_ci if (!ucb->ts_idev) { 3258c2ecf20Sopenharmony_ci error = -ENOMEM; 3268c2ecf20Sopenharmony_ci goto err; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Only in case the IRQ line wasn't supplied, try detecting it */ 3308c2ecf20Sopenharmony_ci if (ucb->irq < 0) { 3318c2ecf20Sopenharmony_ci error = ucb1400_ts_detect_irq(ucb, pdev); 3328c2ecf20Sopenharmony_ci if (error) { 3338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ probe failed\n"); 3348c2ecf20Sopenharmony_ci goto err_free_devs; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "found IRQ %d\n", ucb->irq); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci init_waitqueue_head(&ucb->ts_wait); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci input_set_drvdata(ucb->ts_idev, ucb); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ucb->ts_idev->dev.parent = &pdev->dev; 3448c2ecf20Sopenharmony_ci ucb->ts_idev->name = "UCB1400 touchscreen interface"; 3458c2ecf20Sopenharmony_ci ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97, 3468c2ecf20Sopenharmony_ci AC97_VENDOR_ID1); 3478c2ecf20Sopenharmony_ci ucb->ts_idev->id.product = ucb->id; 3488c2ecf20Sopenharmony_ci ucb->ts_idev->open = ucb1400_ts_open; 3498c2ecf20Sopenharmony_ci ucb->ts_idev->close = ucb1400_ts_close; 3508c2ecf20Sopenharmony_ci ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); 3518c2ecf20Sopenharmony_ci ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * Enable ADC filter to prevent horrible jitter on Colibri. 3558c2ecf20Sopenharmony_ci * This also further reduces jitter on boards where ADCSYNC 3568c2ecf20Sopenharmony_ci * pin is connected. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci fcsr = ucb1400_reg_read(ucb->ac97, UCB_FCSR); 3598c2ecf20Sopenharmony_ci ucb1400_reg_write(ucb->ac97, UCB_FCSR, fcsr | UCB_FCSR_AVE); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci ucb1400_adc_enable(ucb->ac97); 3628c2ecf20Sopenharmony_ci x_res = ucb1400_ts_read_xres(ucb); 3638c2ecf20Sopenharmony_ci y_res = ucb1400_ts_read_yres(ucb); 3648c2ecf20Sopenharmony_ci ucb1400_adc_disable(ucb->ac97); 3658c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "x/y = %d/%d\n", x_res, y_res); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0); 3688c2ecf20Sopenharmony_ci input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0); 3698c2ecf20Sopenharmony_ci input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ucb1400_ts_stop(ucb); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq, 3748c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 3758c2ecf20Sopenharmony_ci "UCB1400", ucb); 3768c2ecf20Sopenharmony_ci if (error) { 3778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3788c2ecf20Sopenharmony_ci "unable to grab irq%d: %d\n", ucb->irq, error); 3798c2ecf20Sopenharmony_ci goto err_free_devs; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci error = input_register_device(ucb->ts_idev); 3838c2ecf20Sopenharmony_ci if (error) 3848c2ecf20Sopenharmony_ci goto err_free_irq; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cierr_free_irq: 3898c2ecf20Sopenharmony_ci free_irq(ucb->irq, ucb); 3908c2ecf20Sopenharmony_cierr_free_devs: 3918c2ecf20Sopenharmony_ci input_free_device(ucb->ts_idev); 3928c2ecf20Sopenharmony_cierr: 3938c2ecf20Sopenharmony_ci return error; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int ucb1400_ts_remove(struct platform_device *pdev) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci free_irq(ucb->irq, ucb); 4018c2ecf20Sopenharmony_ci input_unregister_device(ucb->ts_idev); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int __maybe_unused ucb1400_ts_suspend(struct device *dev) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct ucb1400_ts *ucb = dev_get_platdata(dev); 4098c2ecf20Sopenharmony_ci struct input_dev *idev = ucb->ts_idev; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci mutex_lock(&idev->mutex); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (idev->users) 4148c2ecf20Sopenharmony_ci ucb1400_ts_stop(ucb); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci mutex_unlock(&idev->mutex); 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int __maybe_unused ucb1400_ts_resume(struct device *dev) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct ucb1400_ts *ucb = dev_get_platdata(dev); 4238c2ecf20Sopenharmony_ci struct input_dev *idev = ucb->ts_idev; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci mutex_lock(&idev->mutex); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (idev->users) 4288c2ecf20Sopenharmony_ci ucb1400_ts_start(ucb); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci mutex_unlock(&idev->mutex); 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops, 4358c2ecf20Sopenharmony_ci ucb1400_ts_suspend, ucb1400_ts_resume); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic struct platform_driver ucb1400_ts_driver = { 4388c2ecf20Sopenharmony_ci .probe = ucb1400_ts_probe, 4398c2ecf20Sopenharmony_ci .remove = ucb1400_ts_remove, 4408c2ecf20Sopenharmony_ci .driver = { 4418c2ecf20Sopenharmony_ci .name = "ucb1400_ts", 4428c2ecf20Sopenharmony_ci .pm = &ucb1400_ts_pm_ops, 4438c2ecf20Sopenharmony_ci }, 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_cimodule_platform_driver(ucb1400_ts_driver); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cimodule_param(adcsync, bool, 0444); 4488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cimodule_param(ts_delay, int, 0444); 4518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ts_delay, "Delay between panel setup and" 4528c2ecf20Sopenharmony_ci " position read. Default = 55us."); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cimodule_param(ts_delay_pressure, int, 0444); 4558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ts_delay_pressure, 4568c2ecf20Sopenharmony_ci "delay between panel setup and pressure read." 4578c2ecf20Sopenharmony_ci " Default = 0us."); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips UCB1400 touchscreen driver"); 4608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 461