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
|
@ -66,7 +66,6 @@ struct ts_event {
|
||||||
struct tsc2007 {
|
struct tsc2007 {
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
char phys[32];
|
char phys[32];
|
||||||
struct delayed_work work;
|
|
||||||
|
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
@ -76,9 +75,11 @@ struct tsc2007 {
|
||||||
unsigned long poll_delay;
|
unsigned long poll_delay;
|
||||||
unsigned long poll_period;
|
unsigned long poll_period;
|
||||||
|
|
||||||
bool pendown;
|
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
|
wait_queue_head_t wait;
|
||||||
|
bool stopped;
|
||||||
|
|
||||||
int (*get_pendown_state)(void);
|
int (*get_pendown_state)(void);
|
||||||
void (*clear_penirq)(void);
|
void (*clear_penirq)(void);
|
||||||
};
|
};
|
||||||
|
@ -141,25 +142,8 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
|
||||||
return rt;
|
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
|
* NOTE: We can't rely on the pressure to determine the pen down
|
||||||
* state, even though this controller has a pressure sensor.
|
* 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
|
* 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
|
* work function by reading the pen signal state (it's a GPIO
|
||||||
* and IRQ). Unfortunately such callback is not always available,
|
* 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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
struct tsc2007 *ts = handle;
|
||||||
|
|
||||||
if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
|
if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
|
||||||
disable_irq_nosync(ts->irq);
|
return IRQ_WAKE_THREAD;
|
||||||
schedule_delayed_work(&ts->work,
|
|
||||||
msecs_to_jiffies(ts->poll_delay));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ts->clear_penirq)
|
if (ts->clear_penirq)
|
||||||
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)
|
static void tsc2007_free_irq(struct tsc2007 *ts)
|
||||||
{
|
{
|
||||||
|
ts->stopped = true;
|
||||||
|
mb();
|
||||||
|
wake_up(&ts->wait);
|
||||||
free_irq(ts->irq, ts);
|
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,
|
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->client = client;
|
||||||
ts->irq = client->irq;
|
ts->irq = client->irq;
|
||||||
ts->input = input_dev;
|
ts->input = input_dev;
|
||||||
INIT_DELAYED_WORK(&ts->work, tsc2007_work);
|
init_waitqueue_head(&ts->wait);
|
||||||
|
|
||||||
ts->model = pdata->model;
|
ts->model = pdata->model;
|
||||||
ts->x_plate_ohms = pdata->x_plate_ohms;
|
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)
|
if (pdata->init_platform_hw)
|
||||||
pdata->init_platform_hw();
|
pdata->init_platform_hw();
|
||||||
|
|
||||||
err = request_irq(ts->irq, tsc2007_irq, 0,
|
err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
|
||||||
client->dev.driver->name, ts);
|
IRQF_ONESHOT, client->dev.driver->name, ts);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&client->dev, "irq %d busy?\n", ts->irq);
|
dev_err(&client->dev, "irq %d busy?\n", ts->irq);
|
||||||
goto err_free_mem;
|
goto err_free_mem;
|
||||||
|
|
Loading…
Reference in New Issue