Input: tsc2007 - convert to threaded IRQ
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>
This commit is contained in:
parent
6a20baa9a4
commit
377dc5538c
1 changed files with 78 additions and 96 deletions
|
@ -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)
|
||||
static bool tsc2007_is_pen_down(struct tsc2007 *ts)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
if (rt) {
|
||||
struct input_dev *input = ts->input;
|
||||
|
||||
if (!ts->pendown) {
|
||||
dev_dbg(&ts->client->dev, "DOWN\n");
|
||||
|
||||
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);
|
||||
|
||||
dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
|
||||
tc.x, tc.y, rt);
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ts->pendown || debounced)
|
||||
schedule_delayed_work(&ts->work,
|
||||
msecs_to_jiffies(ts->poll_period));
|
||||
else
|
||||
enable_irq(ts->irq);
|
||||
return ts->get_pendown_state();
|
||||
}
|
||||
|
||||
static irqreturn_t tsc2007_irq(int irq, void *handle)
|
||||
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);
|
||||
|
||||
rt = tsc2007_calculate_pressure(ts, &tc);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
wait_event_timeout(ts->wait, ts->stopped,
|
||||
msecs_to_jiffies(ts->poll_period));
|
||||
}
|
||||
|
||||
dev_dbg(&ts->client->dev, "UP\n");
|
||||
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
input_sync(input);
|
||||
|
||||
if (ts->clear_penirq)
|
||||
ts->clear_penirq();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
Reference in a new issue