root/drivers/mfd/ucb1x00-ts.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ucb1x00_ts_evt_add
  2. ucb1x00_ts_event_release
  3. ucb1x00_ts_mode_int
  4. ucb1x00_ts_read_pressure
  5. ucb1x00_ts_read_xpos
  6. ucb1x00_ts_read_ypos
  7. ucb1x00_ts_read_xres
  8. ucb1x00_ts_read_yres
  9. ucb1x00_ts_pen_down
  10. ucb1x00_thread
  11. ucb1x00_ts_irq
  12. ucb1x00_ts_open
  13. ucb1x00_ts_close
  14. ucb1x00_ts_add
  15. ucb1x00_ts_remove
  16. ucb1x00_ts_init
  17. ucb1x00_ts_exit

   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 
  38 struct 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 
  52 static int adcsync;
  53 
  54 static 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 
  65 static 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  */
  77 static 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  */
  89 static 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  */
 116 static 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  */
 143 static 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  */
 169 static 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  */
 181 static 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 
 189 static 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  */
 204 static 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  */
 288 static 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 
 301 static 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  */
 347 static 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  */
 364 static 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 
 416 static 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 
 424 static struct ucb1x00_driver ucb1x00_ts_driver = {
 425         .add            = ucb1x00_ts_add,
 426         .remove         = ucb1x00_ts_remove,
 427 };
 428 
 429 static int __init ucb1x00_ts_init(void)
 430 {
 431         return ucb1x00_register_driver(&ucb1x00_ts_driver);
 432 }
 433 
 434 static void __exit ucb1x00_ts_exit(void)
 435 {
 436         ucb1x00_unregister_driver(&ucb1x00_ts_driver);
 437 }
 438 
 439 module_param(adcsync, int, 0444);
 440 module_init(ucb1x00_ts_init);
 441 module_exit(ucb1x00_ts_exit);
 442 
 443 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 444 MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
 445 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */