ucb1x00-ts.c (10943B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Touchscreen driver for UCB1x00-based touchscreens 4 * 5 * Copyright (C) 2001 Russell King, All Rights Reserved. 6 * Copyright (C) 2005 Pavel Machek 7 * 8 * 21-Jan-2002 <jco@ict.es> : 9 * 10 * Added support for synchronous A/D mode. This mode is useful to 11 * avoid noise induced in the touchpanel by the LCD, provided that 12 * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. 13 * It is important to note that the signal connected to the ADCSYNC 14 * pin should provide pulses even when the LCD is blanked, otherwise 15 * a pen touch needed to unblank the LCD will never be read. 16 */ 17#include <linux/module.h> 18#include <linux/moduleparam.h> 19#include <linux/init.h> 20#include <linux/interrupt.h> 21#include <linux/sched.h> 22#include <linux/spinlock.h> 23#include <linux/completion.h> 24#include <linux/delay.h> 25#include <linux/string.h> 26#include <linux/input.h> 27#include <linux/device.h> 28#include <linux/freezer.h> 29#include <linux/slab.h> 30#include <linux/kthread.h> 31#include <linux/mfd/ucb1x00.h> 32 33#include <mach/collie.h> 34#include <asm/mach-types.h> 35 36 37 38struct ucb1x00_ts { 39 struct input_dev *idev; 40 struct ucb1x00 *ucb; 41 42 spinlock_t irq_lock; 43 unsigned irq_disabled; 44 wait_queue_head_t irq_wait; 45 struct task_struct *rtask; 46 u16 x_res; 47 u16 y_res; 48 49 unsigned int adcsync:1; 50}; 51 52static int adcsync; 53 54static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) 55{ 56 struct input_dev *idev = ts->idev; 57 58 input_report_abs(idev, ABS_X, x); 59 input_report_abs(idev, ABS_Y, y); 60 input_report_abs(idev, ABS_PRESSURE, pressure); 61 input_report_key(idev, BTN_TOUCH, 1); 62 input_sync(idev); 63} 64 65static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) 66{ 67 struct input_dev *idev = ts->idev; 68 69 input_report_abs(idev, ABS_PRESSURE, 0); 70 input_report_key(idev, BTN_TOUCH, 0); 71 input_sync(idev); 72} 73 74/* 75 * Switch to interrupt mode. 76 */ 77static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) 78{ 79 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 80 UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | 81 UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | 82 UCB_TS_CR_MODE_INT); 83} 84 85/* 86 * Switch to pressure mode, and read pressure. We don't need to wait 87 * here, since both plates are being driven. 88 */ 89static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) 90{ 91 if (machine_is_collie()) { 92 ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0); 93 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 94 UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW | 95 UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); 96 97 udelay(55); 98 99 return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_AD2, ts->adcsync); 100 } else { 101 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 102 UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | 103 UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | 104 UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 105 106 return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); 107 } 108} 109 110/* 111 * Switch to X position mode and measure Y plate. We switch the plate 112 * configuration in pressure mode, then switch to position mode. This 113 * gives a faster response time. Even so, we need to wait about 55us 114 * for things to stabilise. 115 */ 116static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) 117{ 118 if (machine_is_collie()) 119 ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); 120 else { 121 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 122 UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 123 UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 124 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 125 UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 126 UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 127 } 128 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 129 UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 130 UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); 131 132 udelay(55); 133 134 return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); 135} 136 137/* 138 * Switch to Y position mode and measure X plate. We switch the plate 139 * configuration in pressure mode, then switch to position mode. This 140 * gives a faster response time. Even so, we need to wait about 55us 141 * for things to stabilise. 142 */ 143static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) 144{ 145 if (machine_is_collie()) 146 ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); 147 else { 148 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 149 UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 150 UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 151 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 152 UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 153 UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 154 } 155 156 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 157 UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 158 UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); 159 160 udelay(55); 161 162 return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); 163} 164 165/* 166 * Switch to X plate resistance mode. Set MX to ground, PX to 167 * supply. Measure current. 168 */ 169static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) 170{ 171 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 172 UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | 173 UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 174 return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); 175} 176 177/* 178 * Switch to Y plate resistance mode. Set MY to ground, PY to 179 * supply. Measure current. 180 */ 181static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) 182{ 183 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 184 UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | 185 UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 186 return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); 187} 188 189static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts) 190{ 191 unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); 192 193 if (machine_is_collie()) 194 return (!(val & (UCB_TS_CR_TSPX_LOW))); 195 else 196 return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)); 197} 198 199/* 200 * This is a RT kernel thread that handles the ADC accesses 201 * (mainly so we can use semaphores in the UCB1200 core code 202 * to serialise accesses to the ADC). 203 */ 204static int ucb1x00_thread(void *_ts) 205{ 206 struct ucb1x00_ts *ts = _ts; 207 DECLARE_WAITQUEUE(wait, current); 208 bool frozen, ignore = false; 209 int valid = 0; 210 211 set_freezable(); 212 add_wait_queue(&ts->irq_wait, &wait); 213 while (!kthread_freezable_should_stop(&frozen)) { 214 unsigned int x, y, p; 215 signed long timeout; 216 217 if (frozen) 218 ignore = true; 219 220 ucb1x00_adc_enable(ts->ucb); 221 222 x = ucb1x00_ts_read_xpos(ts); 223 y = ucb1x00_ts_read_ypos(ts); 224 p = ucb1x00_ts_read_pressure(ts); 225 226 /* 227 * Switch back to interrupt mode. 228 */ 229 ucb1x00_ts_mode_int(ts); 230 ucb1x00_adc_disable(ts->ucb); 231 232 msleep(10); 233 234 ucb1x00_enable(ts->ucb); 235 236 237 if (ucb1x00_ts_pen_down(ts)) { 238 set_current_state(TASK_INTERRUPTIBLE); 239 240 spin_lock_irq(&ts->irq_lock); 241 if (ts->irq_disabled) { 242 ts->irq_disabled = 0; 243 enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX); 244 } 245 spin_unlock_irq(&ts->irq_lock); 246 ucb1x00_disable(ts->ucb); 247 248 /* 249 * If we spat out a valid sample set last time, 250 * spit out a "pen off" sample here. 251 */ 252 if (valid) { 253 ucb1x00_ts_event_release(ts); 254 valid = 0; 255 } 256 257 timeout = MAX_SCHEDULE_TIMEOUT; 258 } else { 259 ucb1x00_disable(ts->ucb); 260 261 /* 262 * Filtering is policy. Policy belongs in user 263 * space. We therefore leave it to user space 264 * to do any filtering they please. 265 */ 266 if (!ignore) { 267 ucb1x00_ts_evt_add(ts, p, x, y); 268 valid = 1; 269 } 270 271 set_current_state(TASK_INTERRUPTIBLE); 272 timeout = HZ / 100; 273 } 274 275 schedule_timeout(timeout); 276 } 277 278 remove_wait_queue(&ts->irq_wait, &wait); 279 280 ts->rtask = NULL; 281 return 0; 282} 283 284/* 285 * We only detect touch screen _touches_ with this interrupt 286 * handler, and even then we just schedule our task. 287 */ 288static irqreturn_t ucb1x00_ts_irq(int irq, void *id) 289{ 290 struct ucb1x00_ts *ts = id; 291 292 spin_lock(&ts->irq_lock); 293 ts->irq_disabled = 1; 294 disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX); 295 spin_unlock(&ts->irq_lock); 296 wake_up(&ts->irq_wait); 297 298 return IRQ_HANDLED; 299} 300 301static int ucb1x00_ts_open(struct input_dev *idev) 302{ 303 struct ucb1x00_ts *ts = input_get_drvdata(idev); 304 unsigned long flags = 0; 305 int ret = 0; 306 307 BUG_ON(ts->rtask); 308 309 if (machine_is_collie()) 310 flags = IRQF_TRIGGER_RISING; 311 else 312 flags = IRQF_TRIGGER_FALLING; 313 314 ts->irq_disabled = 0; 315 316 init_waitqueue_head(&ts->irq_wait); 317 ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq, 318 flags, "ucb1x00-ts", ts); 319 if (ret < 0) 320 goto out; 321 322 /* 323 * If we do this at all, we should allow the user to 324 * measure and read the X and Y resistance at any time. 325 */ 326 ucb1x00_adc_enable(ts->ucb); 327 ts->x_res = ucb1x00_ts_read_xres(ts); 328 ts->y_res = ucb1x00_ts_read_yres(ts); 329 ucb1x00_adc_disable(ts->ucb); 330 331 ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd"); 332 if (!IS_ERR(ts->rtask)) { 333 ret = 0; 334 } else { 335 free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); 336 ts->rtask = NULL; 337 ret = -EFAULT; 338 } 339 340 out: 341 return ret; 342} 343 344/* 345 * Release touchscreen resources. Disable IRQs. 346 */ 347static void ucb1x00_ts_close(struct input_dev *idev) 348{ 349 struct ucb1x00_ts *ts = input_get_drvdata(idev); 350 351 if (ts->rtask) 352 kthread_stop(ts->rtask); 353 354 ucb1x00_enable(ts->ucb); 355 free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); 356 ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); 357 ucb1x00_disable(ts->ucb); 358} 359 360 361/* 362 * Initialisation. 363 */ 364static int ucb1x00_ts_add(struct ucb1x00_dev *dev) 365{ 366 struct ucb1x00_ts *ts; 367 struct input_dev *idev; 368 int err; 369 370 ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); 371 idev = input_allocate_device(); 372 if (!ts || !idev) { 373 err = -ENOMEM; 374 goto fail; 375 } 376 377 ts->ucb = dev->ucb; 378 ts->idev = idev; 379 ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; 380 spin_lock_init(&ts->irq_lock); 381 382 idev->name = "Touchscreen panel"; 383 idev->id.product = ts->ucb->id; 384 idev->open = ucb1x00_ts_open; 385 idev->close = ucb1x00_ts_close; 386 idev->dev.parent = &ts->ucb->dev; 387 388 idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); 389 idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 390 391 input_set_drvdata(idev, ts); 392 393 ucb1x00_adc_enable(ts->ucb); 394 ts->x_res = ucb1x00_ts_read_xres(ts); 395 ts->y_res = ucb1x00_ts_read_yres(ts); 396 ucb1x00_adc_disable(ts->ucb); 397 398 input_set_abs_params(idev, ABS_X, 0, ts->x_res, 0, 0); 399 input_set_abs_params(idev, ABS_Y, 0, ts->y_res, 0, 0); 400 input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0); 401 402 err = input_register_device(idev); 403 if (err) 404 goto fail; 405 406 dev->priv = ts; 407 408 return 0; 409 410 fail: 411 input_free_device(idev); 412 kfree(ts); 413 return err; 414} 415 416static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) 417{ 418 struct ucb1x00_ts *ts = dev->priv; 419 420 input_unregister_device(ts->idev); 421 kfree(ts); 422} 423 424static struct ucb1x00_driver ucb1x00_ts_driver = { 425 .add = ucb1x00_ts_add, 426 .remove = ucb1x00_ts_remove, 427}; 428 429static int __init ucb1x00_ts_init(void) 430{ 431 return ucb1x00_register_driver(&ucb1x00_ts_driver); 432} 433 434static void __exit ucb1x00_ts_exit(void) 435{ 436 ucb1x00_unregister_driver(&ucb1x00_ts_driver); 437} 438 439module_param(adcsync, int, 0444); 440module_init(ucb1x00_ts_init); 441module_exit(ucb1x00_ts_exit); 442 443MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); 444MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); 445MODULE_LICENSE("GPL");