Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 377dc553 authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

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: default avatarThierry Reding <thierry.reding@avionic-design.de>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 6a20baa9
Loading
Loading
Loading
Loading
+66 −84
Original line number Diff line number Diff line
@@ -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;

	return ts->get_pendown_state();
}

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 > ts->max_rt) {

		if (rt == 0 && !ts->get_pendown_state) {
			/*
		 * 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 pressure reported is 0 and we don't have
			 * callback to check pendown state, we have to
			 * assume that pen was lifted up.
			 */
		dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
		debounced = true;
		goto out;

			break;
		}

	if (rt) {
		struct input_dev *input = ts->input;

		if (!ts->pendown) {
			dev_dbg(&ts->client->dev, "DOWN\n");
		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);

		dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
			tc.x, tc.y, rt);

	} else if (!ts->get_pendown_state && ts->pendown) {
		} else {
			/*
		 * 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.
			 * 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.
			 */
		tsc2007_send_up_event(ts);
		ts->pendown = false;
			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
		}

 out:
	if (ts->pendown || debounced)
		schedule_delayed_work(&ts->work,
		wait_event_timeout(ts->wait, ts->stopped,
				   msecs_to_jiffies(ts->poll_period));
	else
		enable_irq(ts->irq);
	}

static irqreturn_t tsc2007_irq(int irq, void *handle)
	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;