Input: tsc2007 - convert to threaded IRQ
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 25 Aug 2011 07:25:12 +0000 (00:25 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 27 Aug 2011 06:21:53 +0000 (23:21 -0700)
Instead of using hard IRQ and workqueue solution switch to using threaded
interrupt handler to simplify the code and locking rules.

Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/touchscreen/tsc2007.c

index fadc115..e3aaf50 100644 (file)
@@ -66,7 +66,6 @@ struct ts_event {
 struct tsc2007 {
        struct input_dev        *input;
        char                    phys[32];
-       struct delayed_work     work;
 
        struct i2c_client       *client;
 
@@ -76,9 +75,11 @@ struct tsc2007 {
        unsigned long           poll_delay;
        unsigned long           poll_period;
 
-       bool                    pendown;
        int                     irq;
 
+       wait_queue_head_t       wait;
+       bool                    stopped;
+
        int                     (*get_pendown_state)(void);
        void                    (*clear_penirq)(void);
 };
@@ -141,25 +142,8 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
        return rt;
 }
 
-static void tsc2007_send_up_event(struct tsc2007 *tsc)
-{
-       struct input_dev *input = tsc->input;
-
-       dev_dbg(&tsc->client->dev, "UP\n");
-
-       input_report_key(input, BTN_TOUCH, 0);
-       input_report_abs(input, ABS_PRESSURE, 0);
-       input_sync(input);
-}
-
-static void tsc2007_work(struct work_struct *work)
+static bool tsc2007_is_pen_down(struct tsc2007 *ts)
 {
-       struct tsc2007 *ts =
-               container_of(to_delayed_work(work), struct tsc2007, work);
-       bool debounced = false;
-       struct ts_event tc;
-       u32 rt;
-
        /*
         * NOTE: We can't rely on the pressure to determine the pen down
         * state, even though this controller has a pressure sensor.
@@ -170,79 +154,82 @@ static void tsc2007_work(struct work_struct *work)
         * The only safe way to check for the pen up condition is in the
         * work function by reading the pen signal state (it's a GPIO
         * and IRQ). Unfortunately such callback is not always available,
-        * in that case we have rely on the pressure anyway.
+        * in that case we assume that the pen is down and expect caller
+        * to fall back on the pressure reading.
         */
-       if (ts->get_pendown_state) {
-               if (unlikely(!ts->get_pendown_state())) {
-                       tsc2007_send_up_event(ts);
-                       ts->pendown = false;
-                       goto out;
-               }
 
-               dev_dbg(&ts->client->dev, "pen is still down\n");
-       }
+       if (!ts->get_pendown_state)
+               return true;
 
-       tsc2007_read_values(ts, &tc);
+       return ts->get_pendown_state();
+}
 
-       rt = tsc2007_calculate_pressure(ts, &tc);
-       if (rt > ts->max_rt) {
-               /*
-                * Sample found inconsistent by debouncing or pressure is
-                * beyond the maximum. Don't report it to user space,
-                * repeat at least once more the measurement.
-                */
-               dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
-               debounced = true;
-               goto out;
+static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
+{
+       struct tsc2007 *ts = handle;
+       struct input_dev *input = ts->input;
+       struct ts_event tc;
+       u32 rt;
 
-       }
+       while (!ts->stopped && tsc2007_is_pen_down(ts)) {
+
+               /* pen is down, continue with the measurement */
+               tsc2007_read_values(ts, &tc);
 
-       if (rt) {
-               struct input_dev *input = ts->input;
+               rt = tsc2007_calculate_pressure(ts, &tc);
 
-               if (!ts->pendown) {
-                       dev_dbg(&ts->client->dev, "DOWN\n");
+               if (rt == 0 && !ts->get_pendown_state) {
+                       /*
+                        * If pressure reported is 0 and we don't have
+                        * callback to check pendown state, we have to
+                        * assume that pen was lifted up.
+                        */
+                       break;
+               }
+
+               if (rt <= ts->max_rt) {
+                       dev_dbg(&ts->client->dev,
+                               "DOWN point(%4d,%4d), pressure (%4u)\n",
+                               tc.x, tc.y, rt);
 
                        input_report_key(input, BTN_TOUCH, 1);
-                       ts->pendown = true;
+                       input_report_abs(input, ABS_X, tc.x);
+                       input_report_abs(input, ABS_Y, tc.y);
+                       input_report_abs(input, ABS_PRESSURE, rt);
+
+                       input_sync(input);
+
+               } else {
+                       /*
+                        * Sample found inconsistent by debouncing or pressure is
+                        * beyond the maximum. Don't report it to user space,
+                        * repeat at least once more the measurement.
+                        */
+                       dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
                }
 
-               input_report_abs(input, ABS_X, tc.x);
-               input_report_abs(input, ABS_Y, tc.y);
-               input_report_abs(input, ABS_PRESSURE, rt);
+               wait_event_timeout(ts->wait, ts->stopped,
+                                  msecs_to_jiffies(ts->poll_period));
+       }
 
-               input_sync(input);
+       dev_dbg(&ts->client->dev, "UP\n");
 
-               dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
-                       tc.x, tc.y, rt);
+       input_report_key(input, BTN_TOUCH, 0);
+       input_report_abs(input, ABS_PRESSURE, 0);
+       input_sync(input);
 
-       } else if (!ts->get_pendown_state && ts->pendown) {
-               /*
-                * We don't have callback to check pendown state, so we
-                * have to assume that since pressure reported is 0 the
-                * pen was lifted up.
-                */
-               tsc2007_send_up_event(ts);
-               ts->pendown = false;
-       }
+       if (ts->clear_penirq)
+               ts->clear_penirq();
 
- out:
-       if (ts->pendown || debounced)
-               schedule_delayed_work(&ts->work,
-                                     msecs_to_jiffies(ts->poll_period));
-       else
-               enable_irq(ts->irq);
+       return IRQ_HANDLED;
 }
 
-static irqreturn_t tsc2007_irq(int irq, void *handle)
+static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
 {
        struct tsc2007 *ts = handle;
 
-       if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
-               disable_irq_nosync(ts->irq);
-               schedule_delayed_work(&ts->work,
-                                     msecs_to_jiffies(ts->poll_delay));
-       }
+       if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
+               return IRQ_WAKE_THREAD;
 
        if (ts->clear_penirq)
                ts->clear_penirq();
@@ -252,15 +239,10 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
 
 static void tsc2007_free_irq(struct tsc2007 *ts)
 {
+       ts->stopped = true;
+       mb();
+       wake_up(&ts->wait);
        free_irq(ts->irq, ts);
-       if (cancel_delayed_work_sync(&ts->work)) {
-               /*
-                * Work was pending, therefore we need to enable
-                * IRQ here to balance the disable_irq() done in the
-                * interrupt handler.
-                */
-               enable_irq(ts->irq);
-       }
 }
 
 static int __devinit tsc2007_probe(struct i2c_client *client,
@@ -290,7 +272,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
        ts->client = client;
        ts->irq = client->irq;
        ts->input = input_dev;
-       INIT_DELAYED_WORK(&ts->work, tsc2007_work);
+       init_waitqueue_head(&ts->wait);
 
        ts->model             = pdata->model;
        ts->x_plate_ohms      = pdata->x_plate_ohms;
@@ -318,8 +300,8 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
        if (pdata->init_platform_hw)
                pdata->init_platform_hw();
 
-       err = request_irq(ts->irq, tsc2007_irq, 0,
-                       client->dev.driver->name, ts);
+       err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
+                                  IRQF_ONESHOT, client->dev.driver->name, ts);
        if (err < 0) {
                dev_err(&client->dev, "irq %d busy?\n", ts->irq);
                goto err_free_mem;