Commit d570e9ef authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: tsc2007 - make get_pendown_state platform callback optional

In cases when get_pendown_state callback is not available have
the driver to fallback on pressure calculation to determine if
the pen is up.
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 9e3b2583
...@@ -70,7 +70,6 @@ struct tsc2007 { ...@@ -70,7 +70,6 @@ struct tsc2007 {
struct input_dev *input; struct input_dev *input;
char phys[32]; char phys[32];
struct delayed_work work; struct delayed_work work;
struct ts_event tc;
struct i2c_client *client; struct i2c_client *client;
...@@ -106,51 +105,96 @@ static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) ...@@ -106,51 +105,96 @@ static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
return val; return val;
} }
static void tsc2007_send_event(void *tsc) static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
{ {
struct tsc2007 *ts = tsc; /* y- still on; turn on only y+ (and ADC) */
u32 rt; tc->y = tsc2007_xfer(tsc, READ_Y);
u16 x, y, z1, z2;
/* turn y- off, x+ on, then leave in lowpower */
tc->x = tsc2007_xfer(tsc, READ_X);
/* turn y+ off, x- on; we'll use formula #1 */
tc->z1 = tsc2007_xfer(tsc, READ_Z1);
tc->z2 = tsc2007_xfer(tsc, READ_Z2);
x = ts->tc.x; /* Prepare for next touch reading - power down ADC, enable PENIRQ */
y = ts->tc.y; tsc2007_xfer(tsc, PWRDOWN);
z1 = ts->tc.z1; }
z2 = ts->tc.z2;
static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
{
u32 rt = 0;
/* range filtering */ /* range filtering */
if (x == MAX_12BIT) if (tc->x == MAX_12BIT)
x = 0; tc->x = 0;
if (likely(x && z1)) { if (likely(tc->x && tc->z1)) {
/* compute touch pressure resistance using equation #1 */ /* compute touch pressure resistance using equation #1 */
rt = z2; rt = tc->z2 - tc->z1;
rt -= z1; rt *= tc->x;
rt *= x; rt *= tsc->x_plate_ohms;
rt *= ts->x_plate_ohms; rt /= tc->z1;
rt /= z1;
rt = (rt + 2047) >> 12; rt = (rt + 2047) >> 12;
} else
rt = 0;
/*
* 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
*/
if (rt > MAX_12BIT) {
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
return;
} }
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)
{
struct tsc2007 *ts =
container_of(to_delayed_work(work), struct tsc2007, work);
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 this controller has a pressure sensor. The pressure * state, even though this controller has a pressure sensor.
* value can fluctuate for quite a while after lifting the pen and * The pressure value can fluctuate for quite a while after
* in some cases may not even settle at the expected value. * lifting the pen and in some cases may not even settle at the
* expected value.
* *
* 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 and IRQ). * 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.
*/ */
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");
}
tsc2007_read_values(ts, &tc);
rt = tsc2007_calculate_pressure(ts, &tc);
if (rt > MAX_12BIT) {
/*
* 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);
goto out;
}
if (rt) { if (rt) {
struct input_dev *input = ts->input; struct input_dev *input = ts->input;
...@@ -161,68 +205,38 @@ static void tsc2007_send_event(void *tsc) ...@@ -161,68 +205,38 @@ static void tsc2007_send_event(void *tsc)
ts->pendown = true; ts->pendown = true;
} }
input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_X, tc.x);
input_report_abs(input, ABS_Y, y); input_report_abs(input, ABS_Y, tc.y);
input_report_abs(input, ABS_PRESSURE, rt); input_report_abs(input, ABS_PRESSURE, rt);
input_sync(input); input_sync(input);
dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
x, y, rt); tc.x, tc.y, rt);
}
}
static int tsc2007_read_values(struct tsc2007 *tsc)
{
/* y- still on; turn on only y+ (and ADC) */
tsc->tc.y = tsc2007_xfer(tsc, READ_Y);
/* turn y- off, x+ on, then leave in lowpower */
tsc->tc.x = tsc2007_xfer(tsc, READ_X);
/* turn y+ off, x- on; we'll use formula #1 */
tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1);
tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2);
/* Prepare for next touch reading - power down ADC, enable PENIRQ */
tsc2007_xfer(tsc, PWRDOWN);
return 0;
}
static void tsc2007_work(struct work_struct *work)
{
struct tsc2007 *ts =
container_of(to_delayed_work(work), struct tsc2007, work);
if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
struct input_dev *input = ts->input;
dev_dbg(&ts->client->dev, "UP\n");
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; ts->pendown = false;
enable_irq(ts->irq); }
} else {
/* pen is still down, continue with the measurement */
dev_dbg(&ts->client->dev, "pen is still down\n");
tsc2007_read_values(ts);
tsc2007_send_event(ts);
out:
if (ts->pendown)
schedule_delayed_work(&ts->work, schedule_delayed_work(&ts->work,
msecs_to_jiffies(TS_POLL_PERIOD)); msecs_to_jiffies(TS_POLL_PERIOD));
} else
enable_irq(ts->irq);
} }
static irqreturn_t tsc2007_irq(int irq, void *handle) static irqreturn_t tsc2007_irq(int irq, void *handle)
{ {
struct tsc2007 *ts = handle; struct tsc2007 *ts = handle;
if (likely(ts->get_pendown_state())) { if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
disable_irq_nosync(ts->irq); disable_irq_nosync(ts->irq);
schedule_delayed_work(&ts->work, schedule_delayed_work(&ts->work,
msecs_to_jiffies(TS_POLL_DELAY)); msecs_to_jiffies(TS_POLL_DELAY));
...@@ -255,7 +269,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client, ...@@ -255,7 +269,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
struct input_dev *input_dev; struct input_dev *input_dev;
int err; int err;
if (!pdata || !pdata->get_pendown_state) { if (!pdata) {
dev_err(&client->dev, "platform data is required!\n"); dev_err(&client->dev, "platform data is required!\n");
return -EINVAL; return -EINVAL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment