446 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Touchscreen driver for UCB1x00-based touchscreens4 *5 * Copyright (C) 2001 Russell King, All Rights Reserved.6 * Copyright (C) 2005 Pavel Machek7 *8 * 21-Jan-2002 <jco@ict.es> :9 *10 * Added support for synchronous A/D mode. This mode is useful to11 * avoid noise induced in the touchpanel by the LCD, provided that12 * 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 ADCSYNC14 * pin should provide pulses even when the LCD is blanked, otherwise15 * 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 wait87 * 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 plate112 * configuration in pressure mode, then switch to position mode. This113 * gives a faster response time. Even so, we need to wait about 55us114 * 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 plate139 * configuration in pressure mode, then switch to position mode. This140 * gives a faster response time. Even so, we need to wait about 55us141 * 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 to167 * 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 to179 * 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 else196 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 accesses201 * (mainly so we can use semaphores in the UCB1200 core code202 * 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 user263 * space. We therefore leave it to user space264 * 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 interrupt286 * 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 else312 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 to324 * 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");446