ucb1x00-ts.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:15k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/drivers/misc/ucb1x00-ts.c
  3.  *
  4.  *  Copyright (C) 2001 Russell King, All Rights Reserved.
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License version 2 as
  8.  * published by the Free Software Foundation.
  9.  *
  10.  * 21-Jan-2002 <jco@ict.es> :
  11.  *
  12.  * Added support for synchronous A/D mode. This mode is useful to
  13.  * avoid noise induced in the touchpanel by the LCD, provided that
  14.  * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
  15.  * It is important to note that the signal connected to the ADCSYNC
  16.  * pin should provide pulses even when the LCD is blanked, otherwise
  17.  * a pen touch needed to unblank the LCD will never be read.
  18.  */
  19. #include <linux/config.h>
  20. #include <linux/module.h>
  21. #include <linux/init.h>
  22. #include <linux/smp.h>
  23. #include <linux/smp_lock.h>
  24. #include <linux/sched.h>
  25. #include <linux/completion.h>
  26. #include <linux/delay.h>
  27. #include <linux/string.h>
  28. #include <linux/pm.h>
  29. #include <asm/dma.h>
  30. #include <asm/semaphore.h>
  31. #include "ucb1x00.h"
  32. /*
  33.  * Define this if you want the UCB1x00 stuff to talk to the input layer
  34.  */
  35. #ifdef CONFIG_INPUT
  36. #define USE_INPUT
  37. #else
  38. #undef USE_INPUT
  39. #endif
  40. #ifndef USE_INPUT
  41. #include <linux/fs.h>
  42. #include <linux/miscdevice.h>
  43. #include <linux/poll.h>
  44. /*
  45.  * This structure is nonsense - millisecs is not very useful
  46.  * since the field size is too small.  Also, we SHOULD NOT
  47.  * be exposing jiffies to user space directly.
  48.  */
  49. struct ts_event {
  50. u16 pressure;
  51. u16 x;
  52. u16 y;
  53. u16 pad;
  54. struct timeval stamp;
  55. };
  56. #define NR_EVENTS 16
  57. #else
  58. #include <linux/input.h>
  59. #endif
  60. struct ucb1x00_ts {
  61. #ifdef USE_INPUT
  62. struct input_dev idev;
  63. #endif
  64. struct ucb1x00 *ucb;
  65. #ifdef CONFIG_PM
  66. struct pm_dev *pmdev;
  67. #endif
  68. wait_queue_head_t irq_wait;
  69. struct semaphore sem;
  70. struct completion init_exit;
  71. struct task_struct *rtask;
  72. int use_count;
  73. u16 x_res;
  74. u16 y_res;
  75. #ifndef USE_INPUT
  76. struct fasync_struct *fasync;
  77. wait_queue_head_t read_wait;
  78. u8 evt_head;
  79. u8 evt_tail;
  80. struct ts_event events[NR_EVENTS];
  81. #endif
  82. int restart:1;
  83. int adcsync:1;
  84. };
  85. static struct ucb1x00_ts ucbts;
  86. static int adcsync = UCB_NOSYNC;
  87. static int ucb1x00_ts_startup(struct ucb1x00_ts *ts);
  88. static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts);
  89. #ifndef USE_INPUT
  90. #define ucb1x00_ts_evt_pending(ts) ((volatile u8)(ts)->evt_head != (ts)->evt_tail)
  91. #define ucb1x00_ts_evt_get(ts) ((ts)->events + (ts)->evt_tail)
  92. #define ucb1x00_ts_evt_pull(ts) ((ts)->evt_tail = ((ts)->evt_tail + 1) & (NR_EVENTS - 1))
  93. #define ucb1x00_ts_evt_clear(ts) ((ts)->evt_head = (ts)->evt_tail = 0)
  94. static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
  95. {
  96. int next_head;
  97. next_head = (ts->evt_head + 1) & (NR_EVENTS - 1);
  98. if (next_head != ts->evt_tail) {
  99. ts->events[ts->evt_head].pressure = pressure;
  100. ts->events[ts->evt_head].x = x;
  101. ts->events[ts->evt_head].y = y;
  102. do_gettimeofday(&ts->events[ts->evt_head].stamp);
  103. ts->evt_head = next_head;
  104. if (ts->fasync)
  105. kill_fasync(&ts->fasync, SIGIO, POLL_IN);
  106. wake_up_interruptible(&ts->read_wait);
  107. }
  108. }
  109. static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
  110. {
  111. ucb1x00_ts_evt_add(ts, 0, 0, 0);
  112. }
  113. /*
  114.  * User space driver interface.
  115.  */
  116. static ssize_t
  117. ucb1x00_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
  118. {
  119. DECLARE_WAITQUEUE(wait, current);
  120. struct ucb1x00_ts *ts = filp->private_data;
  121. char *ptr = buffer;
  122. int err = 0;
  123. add_wait_queue(&ts->read_wait, &wait);
  124. while (count >= sizeof(struct ts_event)) {
  125. err = -ERESTARTSYS;
  126. if (signal_pending(current))
  127. break;
  128. if (ucb1x00_ts_evt_pending(ts)) {
  129. struct ts_event *evt = ucb1x00_ts_evt_get(ts);
  130. err = copy_to_user(ptr, evt, sizeof(struct ts_event));
  131. ucb1x00_ts_evt_pull(ts);
  132. if (err)
  133. break;
  134. ptr += sizeof(struct ts_event);
  135. count -= sizeof(struct ts_event);
  136. continue;
  137. }
  138. set_current_state(TASK_INTERRUPTIBLE);
  139. err = -EAGAIN;
  140. if (filp->f_flags & O_NONBLOCK)
  141. break;
  142. schedule();
  143. }
  144. current->state = TASK_RUNNING;
  145. remove_wait_queue(&ts->read_wait, &wait);
  146.  
  147. return ptr == buffer ? err : ptr - buffer;
  148. }
  149. static unsigned int ucb1x00_ts_poll(struct file *filp, poll_table *wait)
  150. {
  151. struct ucb1x00_ts *ts = filp->private_data;
  152. int ret = 0;
  153. poll_wait(filp, &ts->read_wait, wait);
  154. if (ucb1x00_ts_evt_pending(ts))
  155. ret = POLLIN | POLLRDNORM;
  156. return ret;
  157. }
  158. static int ucb1x00_ts_fasync(int fd, struct file *filp, int on)
  159. {
  160. struct ucb1x00_ts *ts = filp->private_data;
  161. return fasync_helper(fd, filp, on, &ts->fasync);
  162. }
  163. static int ucb1x00_ts_open(struct inode *inode, struct file *filp)
  164. {
  165. struct ucb1x00_ts *ts = &ucbts;
  166. int ret = 0;
  167. ret = ucb1x00_ts_startup(ts);
  168. if (ret == 0)
  169. filp->private_data = ts;
  170. return ret;
  171. }
  172. /*
  173.  * Release touchscreen resources.  Disable IRQs.
  174.  */
  175. static int ucb1x00_ts_release(struct inode *inode, struct file *filp)
  176. {
  177. struct ucb1x00_ts *ts = filp->private_data;
  178. down(&ts->sem);
  179. ucb1x00_ts_fasync(-1, filp, 0);
  180. ucb1x00_ts_shutdown(ts);
  181. up(&ts->sem);
  182. return 0;
  183. }
  184. static struct file_operations ucb1x00_fops = {
  185. owner: THIS_MODULE,
  186. read: ucb1x00_ts_read,
  187. poll: ucb1x00_ts_poll,
  188. open: ucb1x00_ts_open,
  189. release: ucb1x00_ts_release,
  190. fasync: ucb1x00_ts_fasync,
  191. };
  192. /*
  193.  * The official UCB1x00 touchscreen is a miscdevice:
  194.  *   10 char        Non-serial mice, misc features
  195.  *                   14 = /dev/touchscreen/ucb1x00  UCB 1x00 touchscreen
  196.  */
  197. static struct miscdevice ucb1x00_ts_dev = {
  198. minor: 14,
  199. name: "touchscreen/ucb1x00",
  200. fops: &ucb1x00_fops,
  201. };
  202. static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts)
  203. {
  204. init_waitqueue_head(&ts->read_wait);
  205. return misc_register(&ucb1x00_ts_dev);
  206. }
  207. static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts)
  208. {
  209. misc_deregister(&ucb1x00_ts_dev);
  210. }
  211. #else
  212. #define ucb1x00_ts_evt_clear(ts) do { } while (0)
  213. static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
  214. {
  215. input_report_abs(&ts->idev, ABS_X, x);
  216. input_report_abs(&ts->idev, ABS_Y, y);
  217. input_report_abs(&ts->idev, ABS_PRESSURE, pressure);
  218. }
  219. static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
  220. {
  221. input_report_abs(&ts->idev, ABS_PRESSURE, 0);
  222. }
  223. static int ucb1x00_ts_open(struct input_dev *idev)
  224. {
  225. struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
  226. return ucb1x00_ts_startup(ts);
  227. }
  228. static void ucb1x00_ts_close(struct input_dev *idev)
  229. {
  230. struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
  231. down(&ts->sem);
  232. ucb1x00_ts_shutdown(ts);
  233. up(&ts->sem);
  234. }
  235. static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts)
  236. {
  237. ts->idev.name      = "Touchscreen panel";
  238. ts->idev.idproduct = ts->ucb->id;
  239. ts->idev.open      = ucb1x00_ts_open;
  240. ts->idev.close     = ucb1x00_ts_close;
  241. __set_bit(EV_ABS, ts->idev.evbit);
  242. __set_bit(ABS_X, ts->idev.absbit);
  243. __set_bit(ABS_Y, ts->idev.absbit);
  244. __set_bit(ABS_PRESSURE, ts->idev.absbit);
  245. input_register_device(&ts->idev);
  246. return 0;
  247. }
  248. static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts)
  249. {
  250. input_unregister_device(&ts->idev);
  251. }
  252. #endif
  253. /*
  254.  * Switch to interrupt mode.
  255.  */
  256. static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
  257. {
  258. if (ts->ucb->id == UCB_ID_1400_BUGGY)
  259. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  260. UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
  261. UCB_TS_CR_MODE_INT);
  262. else
  263. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  264. UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
  265. UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
  266. UCB_TS_CR_MODE_INT);
  267. }
  268. /*
  269.  * Switch to pressure mode, and read pressure.  We don't need to wait
  270.  * here, since both plates are being driven.
  271.  */
  272. static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
  273. {
  274. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  275. UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
  276. UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
  277. UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
  278. return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
  279. }
  280. /*
  281.  * Switch to X position mode and measure Y plate.  We switch the plate
  282.  * configuration in pressure mode, then switch to position mode.  This
  283.  * gives a faster response time.  Even so, we need to wait about 55us
  284.  * for things to stabilise.
  285.  */
  286. static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
  287. {
  288. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  289. UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
  290. UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
  291. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  292. UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
  293. UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
  294. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  295. UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
  296. UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
  297. udelay(55);
  298. return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
  299. }
  300. /*
  301.  * Switch to Y position mode and measure X plate.  We switch the plate
  302.  * configuration in pressure mode, then switch to position mode.  This
  303.  * gives a faster response time.  Even so, we need to wait about 55us
  304.  * for things to stabilise.
  305.  */
  306. static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
  307. {
  308. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  309. UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
  310. UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
  311. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  312. UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
  313. UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
  314. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  315. UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
  316. UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
  317. udelay(55);
  318. return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
  319. }
  320. /*
  321.  * Switch to X plate resistance mode.  Set MX to ground, PX to
  322.  * supply.  Measure current.
  323.  */
  324. static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
  325. {
  326. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  327. UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
  328. UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
  329. return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
  330. }
  331. /*
  332.  * Switch to Y plate resistance mode.  Set MY to ground, PY to
  333.  * supply.  Measure current.
  334.  */
  335. static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
  336. {
  337. ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
  338. UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
  339. UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
  340. return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
  341. }
  342. /*
  343.  * This is a RT kernel thread that handles the ADC accesses
  344.  * (mainly so we can use semaphores in the UCB1200 core code
  345.  * to serialise accesses to the ADC).  The UCB1400 access
  346.  * functions are expected to be able to sleep as well.
  347.  */
  348. static int ucb1x00_thread(void *_ts)
  349. {
  350. struct ucb1x00_ts *ts = _ts;
  351. struct task_struct *tsk = current;
  352. DECLARE_WAITQUEUE(wait, tsk);
  353. int valid;
  354. ts->rtask = tsk;
  355. daemonize();
  356. reparent_to_init();
  357. strcpy(tsk->comm, "ktsd");
  358. tsk->tty = NULL;
  359. /*
  360.  * We could run as a real-time thread.  However, thus far
  361.  * this doesn't seem to be necessary.
  362.  */
  363. // tsk->policy = SCHED_FIFO;
  364. // tsk->rt_priority = 1;
  365. /* only want to receive SIGKILL */
  366. spin_lock_irq(&tsk->sigmask_lock);
  367. siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
  368. recalc_sigpending(tsk);
  369. spin_unlock_irq(&tsk->sigmask_lock);
  370. complete(&ts->init_exit);
  371. valid = 0;
  372. add_wait_queue(&ts->irq_wait, &wait);
  373. for (;;) {
  374. unsigned int x, y, p, val;
  375. signed long timeout;
  376. ts->restart = 0;
  377. ucb1x00_adc_enable(ts->ucb);
  378. x = ucb1x00_ts_read_xpos(ts);
  379. y = ucb1x00_ts_read_ypos(ts);
  380. p = ucb1x00_ts_read_pressure(ts);
  381. /*
  382.  * Switch back to interrupt mode.
  383.  */
  384. ucb1x00_ts_mode_int(ts);
  385. ucb1x00_adc_disable(ts->ucb);
  386. set_task_state(tsk, TASK_UNINTERRUPTIBLE);
  387. schedule_timeout(HZ / 100);
  388. if (signal_pending(tsk))
  389. break;
  390. ucb1x00_enable(ts->ucb);
  391. val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
  392. if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) {
  393. set_task_state(tsk, TASK_INTERRUPTIBLE);
  394. ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
  395. ucb1x00_disable(ts->ucb);
  396. /*
  397.  * If we spat out a valid sample set last time,
  398.  * spit out a "pen off" sample here.
  399.  */
  400. if (valid) {
  401. ucb1x00_ts_event_release(ts);
  402. valid = 0;
  403. }
  404. timeout = MAX_SCHEDULE_TIMEOUT;
  405. } else {
  406. ucb1x00_disable(ts->ucb);
  407. /*
  408.  * Filtering is policy.  Policy belongs in user
  409.  * space.  We therefore leave it to user space
  410.  * to do any filtering they please.
  411.  */
  412. if (!ts->restart) {
  413. ucb1x00_ts_evt_add(ts, p, x, y);
  414. valid = 1;
  415. }
  416. set_task_state(tsk, TASK_INTERRUPTIBLE);
  417. timeout = HZ / 100;
  418. }
  419. schedule_timeout(timeout);
  420. if (signal_pending(tsk))
  421. break;
  422. }
  423. remove_wait_queue(&ts->irq_wait, &wait);
  424. ts->rtask = NULL;
  425. ucb1x00_ts_evt_clear(ts);
  426. complete_and_exit(&ts->init_exit, 0);
  427. }
  428. /*
  429.  * We only detect touch screen _touches_ with this interrupt
  430.  * handler, and even then we just schedule our task.
  431.  */
  432. static void ucb1x00_ts_irq(int idx, void *id)
  433. {
  434. struct ucb1x00_ts *ts = id;
  435. ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
  436. wake_up(&ts->irq_wait);
  437. }
  438. static int ucb1x00_ts_startup(struct ucb1x00_ts *ts)
  439. {
  440. int ret = 0;
  441. if (down_interruptible(&ts->sem))
  442. return -EINTR;
  443. if (ts->use_count++ != 0)
  444. goto out;
  445. if (ts->rtask)
  446. panic("ucb1x00: rtask running?");
  447. init_waitqueue_head(&ts->irq_wait);
  448. ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
  449. if (ret < 0)
  450. goto out;
  451. /*
  452.  * If we do this at all, we should allow the user to
  453.  * measure and read the X and Y resistance at any time.
  454.  */
  455. ucb1x00_adc_enable(ts->ucb);
  456. ts->x_res = ucb1x00_ts_read_xres(ts);
  457. ts->y_res = ucb1x00_ts_read_yres(ts);
  458. ucb1x00_adc_disable(ts->ucb);
  459. init_completion(&ts->init_exit);
  460. ret = kernel_thread(ucb1x00_thread, ts, 0);
  461. if (ret >= 0) {
  462. wait_for_completion(&ts->init_exit);
  463. ret = 0;
  464. } else {
  465. ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
  466. }
  467.  out:
  468. if (ret)
  469. ts->use_count--;
  470. up(&ts->sem);
  471. return ret;
  472. }
  473. /*
  474.  * Release touchscreen resources.  Disable IRQs.
  475.  */
  476. static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts)
  477. {
  478. if (--ts->use_count == 0) {
  479. if (ts->rtask) {
  480. send_sig(SIGKILL, ts->rtask, 1);
  481. wait_for_completion(&ts->init_exit);
  482. }
  483. ucb1x00_enable(ts->ucb);
  484. ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
  485. ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
  486. ucb1x00_disable(ts->ucb);
  487. }
  488. }
  489. #ifdef CONFIG_PM
  490. static int ucb1x00_ts_pm (struct pm_dev *dev, pm_request_t rqst, void *data)
  491. {
  492. struct ucb1x00_ts *ts = (struct ucb1x00_ts *) (dev->data);
  493. if (rqst == PM_RESUME && ts->rtask != NULL) {
  494. /*
  495.  * Restart the TS thread to ensure the
  496.  * TS interrupt mode is set up again
  497.  * after sleep.
  498.  */
  499. ts->restart = 1;
  500. wake_up(&ts->irq_wait);
  501. }
  502. return 0;
  503. }
  504. #endif
  505. /*
  506.  * Initialisation.
  507.  */
  508. static int __init ucb1x00_ts_init(void)
  509. {
  510. struct ucb1x00_ts *ts = &ucbts;
  511. ts->ucb = ucb1x00_get();
  512. if (!ts->ucb)
  513. return -ENODEV;
  514. ts->adcsync = adcsync;
  515. init_MUTEX(&ts->sem);
  516. #ifdef CONFIG_PM
  517. ts->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ucb1x00_ts_pm);
  518. if (ts->pmdev == NULL)
  519. printk("ucb1x00_ts: unable to register in PM.n");
  520. else
  521. ts->pmdev->data = ts;
  522. #endif
  523. return ucb1x00_ts_register(ts);
  524. }
  525. static void __exit ucb1x00_ts_exit(void)
  526. {
  527. struct ucb1x00_ts *ts = &ucbts;
  528. ucb1x00_ts_deregister(ts);
  529. #ifdef CONFIG_PM
  530. if (ts->pmdev)
  531. pm_unregister(ts->pmdev);
  532. #endif
  533. }
  534. #ifndef MODULE
  535. /*
  536.  * Parse kernel command-line options.
  537.  *
  538.  * syntax : ucbts=[sync|nosync],...
  539.  */
  540. static int __init ucb1x00_ts_setup(char *str)
  541. {
  542. char *p;
  543. while ((p = strsep(&str, ",")) != NULL) {
  544. if (strcmp(p, "sync") == 0)
  545. adcsync = UCB_SYNC;
  546. }
  547. return 1;
  548. }
  549. __setup("ucbts=", ucb1x00_ts_setup);
  550. #else
  551. MODULE_PARM(adcsync, "i");
  552. MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal");
  553. #endif
  554. module_init(ucb1x00_ts_init);
  555. module_exit(ucb1x00_ts_exit);
  556. MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
  557. MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
  558. MODULE_LICENSE("GPL");