diff options
Diffstat (limited to 'drivers/mfd/ucb1x00-ts.c')
| -rw-r--r-- | drivers/mfd/ucb1x00-ts.c | 158 |
1 files changed, 84 insertions, 74 deletions
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 79fd062ccb3..1e0e20c0e08 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -17,40 +17,38 @@ * pin should provide pulses even when the LCD is blanked, otherwise * a pen touch needed to unblank the LCD will never be read. */ -#include <linux/config.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/smp.h> -#include <linux/smp_lock.h> +#include <linux/interrupt.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/input.h> #include <linux/device.h> -#include <linux/suspend.h> +#include <linux/freezer.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/mfd/ucb1x00.h> -#include <asm/dma.h> -#include <asm/semaphore.h> -#include <asm/arch/collie.h> +#include <mach/collie.h> #include <asm/mach-types.h> -#include "ucb1x00.h" struct ucb1x00_ts { struct input_dev *idev; struct ucb1x00 *ucb; + spinlock_t irq_lock; + unsigned irq_disabled; wait_queue_head_t irq_wait; struct task_struct *rtask; u16 x_res; u16 y_res; - unsigned int restart:1; unsigned int adcsync:1; }; @@ -59,16 +57,20 @@ static int adcsync; static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) { struct input_dev *idev = ts->idev; + input_report_abs(idev, ABS_X, x); input_report_abs(idev, ABS_Y, y); input_report_abs(idev, ABS_PRESSURE, pressure); + input_report_key(idev, BTN_TOUCH, 1); input_sync(idev); } static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) { struct input_dev *idev = ts->idev; + input_report_abs(idev, ABS_PRESSURE, 0); + input_report_key(idev, BTN_TOUCH, 0); input_sync(idev); } @@ -190,6 +192,7 @@ static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts) { unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); + if (machine_is_collie()) return (!(val & (UCB_TS_CR_TSPX_LOW))); else @@ -204,25 +207,18 @@ static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts) static int ucb1x00_thread(void *_ts) { struct ucb1x00_ts *ts = _ts; - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); - int valid; - - /* - * We could run as a real-time thread. However, thus far - * this doesn't seem to be necessary. - */ -// tsk->policy = SCHED_FIFO; -// tsk->rt_priority = 1; - - valid = 0; + DECLARE_WAITQUEUE(wait, current); + bool frozen, ignore = false; + int valid = 0; + set_freezable(); add_wait_queue(&ts->irq_wait, &wait); - while (!kthread_should_stop()) { + while (!kthread_freezable_should_stop(&frozen)) { unsigned int x, y, p; signed long timeout; - ts->restart = 0; + if (frozen) + ignore = true; ucb1x00_adc_enable(ts->ucb); @@ -242,9 +238,14 @@ static int ucb1x00_thread(void *_ts) if (ucb1x00_ts_pen_down(ts)) { - set_task_state(tsk, TASK_INTERRUPTIBLE); + set_current_state(TASK_INTERRUPTIBLE); - ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); + spin_lock_irq(&ts->irq_lock); + if (ts->irq_disabled) { + ts->irq_disabled = 0; + enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX); + } + spin_unlock_irq(&ts->irq_lock); ucb1x00_disable(ts->ucb); /* @@ -265,17 +266,15 @@ static int ucb1x00_thread(void *_ts) * space. We therefore leave it to user space * to do any filtering they please. */ - if (!ts->restart) { + if (!ignore) { ucb1x00_ts_evt_add(ts, p, x, y); valid = 1; } - set_task_state(tsk, TASK_INTERRUPTIBLE); + set_current_state(TASK_INTERRUPTIBLE); timeout = HZ / 100; } - try_to_freeze(); - schedule_timeout(timeout); } @@ -289,22 +288,37 @@ static int ucb1x00_thread(void *_ts) * We only detect touch screen _touches_ with this interrupt * handler, and even then we just schedule our task. */ -static void ucb1x00_ts_irq(int idx, void *id) +static irqreturn_t ucb1x00_ts_irq(int irq, void *id) { struct ucb1x00_ts *ts = id; - ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + + spin_lock(&ts->irq_lock); + ts->irq_disabled = 1; + disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX); + spin_unlock(&ts->irq_lock); wake_up(&ts->irq_wait); + + return IRQ_HANDLED; } static int ucb1x00_ts_open(struct input_dev *idev) { - struct ucb1x00_ts *ts = idev->private; + struct ucb1x00_ts *ts = input_get_drvdata(idev); + unsigned long flags = 0; int ret = 0; BUG_ON(ts->rtask); + if (machine_is_collie()) + flags = IRQF_TRIGGER_RISING; + else + flags = IRQF_TRIGGER_FALLING; + + ts->irq_disabled = 0; + init_waitqueue_head(&ts->irq_wait); - ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq, + flags, "ucb1x00-ts", ts); if (ret < 0) goto out; @@ -321,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev) if (!IS_ERR(ts->rtask)) { ret = 0; } else { - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ts->rtask = NULL; ret = -EFAULT; } @@ -335,37 +349,17 @@ static int ucb1x00_ts_open(struct input_dev *idev) */ static void ucb1x00_ts_close(struct input_dev *idev) { - struct ucb1x00_ts *ts = idev->private; + struct ucb1x00_ts *ts = input_get_drvdata(idev); if (ts->rtask) kthread_stop(ts->rtask); ucb1x00_enable(ts->ucb); - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ucb1x00_disable(ts->ucb); } -#ifdef CONFIG_PM -static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) -{ - struct ucb1x00_ts *ts = dev->priv; - - if (ts->rtask != NULL) { - /* - * Restart the TS thread to ensure the - * TS interrupt mode is set up again - * after sleep. - */ - ts->restart = 1; - wake_up(&ts->irq_wait); - } - return 0; -} -#else -#define ucb1x00_ts_resume NULL -#endif - /* * Initialisation. @@ -373,36 +367,53 @@ static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) static int ucb1x00_ts_add(struct ucb1x00_dev *dev) { struct ucb1x00_ts *ts; + struct input_dev *idev; + int err; ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - ts->idev = input_allocate_device(); - if (!ts->idev) { - kfree(ts); - return -ENOMEM; + idev = input_allocate_device(); + if (!ts || !idev) { + err = -ENOMEM; + goto fail; } ts->ucb = dev->ucb; + ts->idev = idev; ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; + spin_lock_init(&ts->irq_lock); - ts->idev->private = ts; - ts->idev->name = "Touchscreen panel"; - ts->idev->id.product = ts->ucb->id; - ts->idev->open = ucb1x00_ts_open; - ts->idev->close = ucb1x00_ts_close; + idev->name = "Touchscreen panel"; + idev->id.product = ts->ucb->id; + idev->open = ucb1x00_ts_open; + idev->close = ucb1x00_ts_close; + idev->dev.parent = &ts->ucb->dev; - __set_bit(EV_ABS, ts->idev->evbit); - __set_bit(ABS_X, ts->idev->absbit); - __set_bit(ABS_Y, ts->idev->absbit); - __set_bit(ABS_PRESSURE, ts->idev->absbit); + idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_register_device(ts->idev); + input_set_drvdata(idev, ts); + + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + input_set_abs_params(idev, ABS_X, 0, ts->x_res, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, ts->y_res, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0); + + err = input_register_device(idev); + if (err) + goto fail; dev->priv = ts; return 0; + + fail: + input_free_device(idev); + kfree(ts); + return err; } static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) @@ -416,7 +427,6 @@ static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) static struct ucb1x00_driver ucb1x00_ts_driver = { .add = ucb1x00_ts_add, .remove = ucb1x00_ts_remove, - .resume = ucb1x00_ts_resume, }; static int __init ucb1x00_ts_init(void) |
