|
@@ -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;
|