0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
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
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
0087
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
0112
0113
0114
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
0139
0140
0141
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
0167
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
0179
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
0201
0202
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
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
0250
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
0263
0264
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
0286
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
0324
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
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
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");