Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  Touchscreen driver for UCB1x00-based touchscreens
0004  *
0005  *  Copyright (C) 2001 Russell King, All Rights Reserved.
0006  *  Copyright (C) 2005 Pavel Machek
0007  *
0008  * 21-Jan-2002 <jco@ict.es> :
0009  *
0010  * Added support for synchronous A/D mode. This mode is useful to
0011  * avoid noise induced in the touchpanel by the LCD, provided that
0012  * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
0013  * It is important to note that the signal connected to the ADCSYNC
0014  * pin should provide pulses even when the LCD is blanked, otherwise
0015  * a pen touch needed to unblank the LCD will never be read.
0016  */
0017 #include <linux/module.h>
0018 #include <linux/moduleparam.h>
0019 #include <linux/init.h>
0020 #include <linux/interrupt.h>
0021 #include <linux/sched.h>
0022 #include <linux/spinlock.h>
0023 #include <linux/completion.h>
0024 #include <linux/delay.h>
0025 #include <linux/string.h>
0026 #include <linux/input.h>
0027 #include <linux/device.h>
0028 #include <linux/freezer.h>
0029 #include <linux/slab.h>
0030 #include <linux/kthread.h>
0031 #include <linux/mfd/ucb1x00.h>
0032 
0033 #include <mach/collie.h>
0034 #include <asm/mach-types.h>
0035 
0036 
0037 
0038 struct ucb1x00_ts {
0039     struct input_dev    *idev;
0040     struct ucb1x00      *ucb;
0041 
0042     spinlock_t      irq_lock;
0043     unsigned        irq_disabled;
0044     wait_queue_head_t   irq_wait;
0045     struct task_struct  *rtask;
0046     u16         x_res;
0047     u16         y_res;
0048 
0049     unsigned int        adcsync:1;
0050 };
0051 
0052 static int adcsync;
0053 
0054 static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
0055 {
0056     struct input_dev *idev = ts->idev;
0057 
0058     input_report_abs(idev, ABS_X, x);
0059     input_report_abs(idev, ABS_Y, y);
0060     input_report_abs(idev, ABS_PRESSURE, pressure);
0061     input_report_key(idev, BTN_TOUCH, 1);
0062     input_sync(idev);
0063 }
0064 
0065 static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
0066 {
0067     struct input_dev *idev = ts->idev;
0068 
0069     input_report_abs(idev, ABS_PRESSURE, 0);
0070     input_report_key(idev, BTN_TOUCH, 0);
0071     input_sync(idev);
0072 }
0073 
0074 /*
0075  * Switch to interrupt mode.
0076  */
0077 static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
0078 {
0079     ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0080             UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
0081             UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
0082             UCB_TS_CR_MODE_INT);
0083 }
0084 
0085 /*
0086  * Switch to pressure mode, and read pressure.  We don't need to wait
0087  * here, since both plates are being driven.
0088  */
0089 static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
0090 {
0091     if (machine_is_collie()) {
0092         ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
0093         ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0094                   UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
0095                   UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
0096 
0097         udelay(55);
0098 
0099         return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_AD2, ts->adcsync);
0100     } else {
0101         ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0102                   UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
0103                   UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
0104                   UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
0105 
0106         return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
0107     }
0108 }
0109 
0110 /*
0111  * Switch to X position mode and measure Y plate.  We switch the plate
0112  * configuration in pressure mode, then switch to position mode.  This
0113  * gives a faster response time.  Even so, we need to wait about 55us
0114  * for things to stabilise.
0115  */
0116 static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
0117 {
0118     if (machine_is_collie())
0119         ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
0120     else {
0121         ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0122                   UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
0123                   UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
0124         ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0125                   UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
0126                   UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
0127     }
0128     ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0129             UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
0130             UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
0131 
0132     udelay(55);
0133 
0134     return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
0135 }
0136 
0137 /*
0138  * Switch to Y position mode and measure X plate.  We switch the plate
0139  * configuration in pressure mode, then switch to position mode.  This
0140  * gives a faster response time.  Even so, we need to wait about 55us
0141  * for things to stabilise.
0142  */
0143 static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
0144 {
0145     if (machine_is_collie())
0146         ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
0147     else {
0148         ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0149                   UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
0150                   UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
0151         ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0152                   UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
0153                   UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
0154     }
0155 
0156     ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0157             UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
0158             UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
0159 
0160     udelay(55);
0161 
0162     return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
0163 }
0164 
0165 /*
0166  * Switch to X plate resistance mode.  Set MX to ground, PX to
0167  * supply.  Measure current.
0168  */
0169 static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
0170 {
0171     ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0172             UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
0173             UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
0174     return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
0175 }
0176 
0177 /*
0178  * Switch to Y plate resistance mode.  Set MY to ground, PY to
0179  * supply.  Measure current.
0180  */
0181 static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
0182 {
0183     ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
0184             UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
0185             UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
0186     return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
0187 }
0188 
0189 static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts)
0190 {
0191     unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
0192 
0193     if (machine_is_collie())
0194         return (!(val & (UCB_TS_CR_TSPX_LOW)));
0195     else
0196         return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW));
0197 }
0198 
0199 /*
0200  * This is a RT kernel thread that handles the ADC accesses
0201  * (mainly so we can use semaphores in the UCB1200 core code
0202  * to serialise accesses to the ADC).
0203  */
0204 static int ucb1x00_thread(void *_ts)
0205 {
0206     struct ucb1x00_ts *ts = _ts;
0207     DECLARE_WAITQUEUE(wait, current);
0208     bool frozen, ignore = false;
0209     int valid = 0;
0210 
0211     set_freezable();
0212     add_wait_queue(&ts->irq_wait, &wait);
0213     while (!kthread_freezable_should_stop(&frozen)) {
0214         unsigned int x, y, p;
0215         signed long timeout;
0216 
0217         if (frozen)
0218             ignore = true;
0219 
0220         ucb1x00_adc_enable(ts->ucb);
0221 
0222         x = ucb1x00_ts_read_xpos(ts);
0223         y = ucb1x00_ts_read_ypos(ts);
0224         p = ucb1x00_ts_read_pressure(ts);
0225 
0226         /*
0227          * Switch back to interrupt mode.
0228          */
0229         ucb1x00_ts_mode_int(ts);
0230         ucb1x00_adc_disable(ts->ucb);
0231 
0232         msleep(10);
0233 
0234         ucb1x00_enable(ts->ucb);
0235 
0236 
0237         if (ucb1x00_ts_pen_down(ts)) {
0238             set_current_state(TASK_INTERRUPTIBLE);
0239 
0240             spin_lock_irq(&ts->irq_lock);
0241             if (ts->irq_disabled) {
0242                 ts->irq_disabled = 0;
0243                 enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX);
0244             }
0245             spin_unlock_irq(&ts->irq_lock);
0246             ucb1x00_disable(ts->ucb);
0247 
0248             /*
0249              * If we spat out a valid sample set last time,
0250              * spit out a "pen off" sample here.
0251              */
0252             if (valid) {
0253                 ucb1x00_ts_event_release(ts);
0254                 valid = 0;
0255             }
0256 
0257             timeout = MAX_SCHEDULE_TIMEOUT;
0258         } else {
0259             ucb1x00_disable(ts->ucb);
0260 
0261             /*
0262              * Filtering is policy.  Policy belongs in user
0263              * space.  We therefore leave it to user space
0264              * to do any filtering they please.
0265              */
0266             if (!ignore) {
0267                 ucb1x00_ts_evt_add(ts, p, x, y);
0268                 valid = 1;
0269             }
0270 
0271             set_current_state(TASK_INTERRUPTIBLE);
0272             timeout = HZ / 100;
0273         }
0274 
0275         schedule_timeout(timeout);
0276     }
0277 
0278     remove_wait_queue(&ts->irq_wait, &wait);
0279 
0280     ts->rtask = NULL;
0281     return 0;
0282 }
0283 
0284 /*
0285  * We only detect touch screen _touches_ with this interrupt
0286  * handler, and even then we just schedule our task.
0287  */
0288 static irqreturn_t ucb1x00_ts_irq(int irq, void *id)
0289 {
0290     struct ucb1x00_ts *ts = id;
0291 
0292     spin_lock(&ts->irq_lock);
0293     ts->irq_disabled = 1;
0294     disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX);
0295     spin_unlock(&ts->irq_lock);
0296     wake_up(&ts->irq_wait);
0297 
0298     return IRQ_HANDLED;
0299 }
0300 
0301 static int ucb1x00_ts_open(struct input_dev *idev)
0302 {
0303     struct ucb1x00_ts *ts = input_get_drvdata(idev);
0304     unsigned long flags = 0;
0305     int ret = 0;
0306 
0307     BUG_ON(ts->rtask);
0308 
0309     if (machine_is_collie())
0310         flags = IRQF_TRIGGER_RISING;
0311     else
0312         flags = IRQF_TRIGGER_FALLING;
0313 
0314     ts->irq_disabled = 0;
0315 
0316     init_waitqueue_head(&ts->irq_wait);
0317     ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq,
0318               flags, "ucb1x00-ts", ts);
0319     if (ret < 0)
0320         goto out;
0321 
0322     /*
0323      * If we do this at all, we should allow the user to
0324      * measure and read the X and Y resistance at any time.
0325      */
0326     ucb1x00_adc_enable(ts->ucb);
0327     ts->x_res = ucb1x00_ts_read_xres(ts);
0328     ts->y_res = ucb1x00_ts_read_yres(ts);
0329     ucb1x00_adc_disable(ts->ucb);
0330 
0331     ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
0332     if (!IS_ERR(ts->rtask)) {
0333         ret = 0;
0334     } else {
0335         free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
0336         ts->rtask = NULL;
0337         ret = -EFAULT;
0338     }
0339 
0340  out:
0341     return ret;
0342 }
0343 
0344 /*
0345  * Release touchscreen resources.  Disable IRQs.
0346  */
0347 static void ucb1x00_ts_close(struct input_dev *idev)
0348 {
0349     struct ucb1x00_ts *ts = input_get_drvdata(idev);
0350 
0351     if (ts->rtask)
0352         kthread_stop(ts->rtask);
0353 
0354     ucb1x00_enable(ts->ucb);
0355     free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
0356     ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
0357     ucb1x00_disable(ts->ucb);
0358 }
0359 
0360 
0361 /*
0362  * Initialisation.
0363  */
0364 static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
0365 {
0366     struct ucb1x00_ts *ts;
0367     struct input_dev *idev;
0368     int err;
0369 
0370     ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
0371     idev = input_allocate_device();
0372     if (!ts || !idev) {
0373         err = -ENOMEM;
0374         goto fail;
0375     }
0376 
0377     ts->ucb = dev->ucb;
0378     ts->idev = idev;
0379     ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
0380     spin_lock_init(&ts->irq_lock);
0381 
0382     idev->name       = "Touchscreen panel";
0383     idev->id.product = ts->ucb->id;
0384     idev->open       = ucb1x00_ts_open;
0385     idev->close      = ucb1x00_ts_close;
0386     idev->dev.parent = &ts->ucb->dev;
0387 
0388     idev->evbit[0]   = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
0389     idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
0390 
0391     input_set_drvdata(idev, ts);
0392 
0393     ucb1x00_adc_enable(ts->ucb);
0394     ts->x_res = ucb1x00_ts_read_xres(ts);
0395     ts->y_res = ucb1x00_ts_read_yres(ts);
0396     ucb1x00_adc_disable(ts->ucb);
0397 
0398     input_set_abs_params(idev, ABS_X, 0, ts->x_res, 0, 0);
0399     input_set_abs_params(idev, ABS_Y, 0, ts->y_res, 0, 0);
0400     input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
0401 
0402     err = input_register_device(idev);
0403     if (err)
0404         goto fail;
0405 
0406     dev->priv = ts;
0407 
0408     return 0;
0409 
0410  fail:
0411     input_free_device(idev);
0412     kfree(ts);
0413     return err;
0414 }
0415 
0416 static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
0417 {
0418     struct ucb1x00_ts *ts = dev->priv;
0419 
0420     input_unregister_device(ts->idev);
0421     kfree(ts);
0422 }
0423 
0424 static struct ucb1x00_driver ucb1x00_ts_driver = {
0425     .add        = ucb1x00_ts_add,
0426     .remove     = ucb1x00_ts_remove,
0427 };
0428 
0429 static int __init ucb1x00_ts_init(void)
0430 {
0431     return ucb1x00_register_driver(&ucb1x00_ts_driver);
0432 }
0433 
0434 static void __exit ucb1x00_ts_exit(void)
0435 {
0436     ucb1x00_unregister_driver(&ucb1x00_ts_driver);
0437 }
0438 
0439 module_param(adcsync, int, 0444);
0440 module_init(ucb1x00_ts_init);
0441 module_exit(ucb1x00_ts_exit);
0442 
0443 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
0444 MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
0445 MODULE_LICENSE("GPL");