| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Touch Screen driver for EETI's I2C connected touch screen panels |
|---|
| 3 | 4 | * Copyright (c) 2009,2018 Daniel Mack <daniel@zonque.org> |
|---|
| .. | .. |
|---|
| 8 | 9 | * Based on migor_ts.c |
|---|
| 9 | 10 | * Copyright (c) 2008 Magnus Damm |
|---|
| 10 | 11 | * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com> |
|---|
| 11 | | - * |
|---|
| 12 | | - * This file is free software; you can redistribute it and/or |
|---|
| 13 | | - * modify it under the terms of the GNU General Public |
|---|
| 14 | | - * License as published by the Free Software Foundation; either |
|---|
| 15 | | - * version 2 of the License, or (at your option) any later version. |
|---|
| 16 | | - * |
|---|
| 17 | | - * This file is distributed in the hope that it will be useful, |
|---|
| 18 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 19 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 20 | | - * General Public License for more details. |
|---|
| 21 | | - * |
|---|
| 22 | | - * You should have received a copy of the GNU General Public |
|---|
| 23 | | - * License along with this library; if not, write to the Free Software |
|---|
| 24 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 25 | 12 | */ |
|---|
| 26 | 13 | |
|---|
| 27 | 14 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 41 | 28 | struct input_dev *input; |
|---|
| 42 | 29 | struct gpio_desc *attn_gpio; |
|---|
| 43 | 30 | struct touchscreen_properties props; |
|---|
| 31 | + struct mutex mutex; |
|---|
| 44 | 32 | bool running; |
|---|
| 45 | 33 | }; |
|---|
| 46 | 34 | |
|---|
| .. | .. |
|---|
| 75 | 63 | input_sync(eeti->input); |
|---|
| 76 | 64 | } |
|---|
| 77 | 65 | |
|---|
| 66 | +static int eeti_ts_read(struct eeti_ts *eeti) |
|---|
| 67 | +{ |
|---|
| 68 | + int len, error; |
|---|
| 69 | + char buf[6]; |
|---|
| 70 | + |
|---|
| 71 | + len = i2c_master_recv(eeti->client, buf, sizeof(buf)); |
|---|
| 72 | + if (len != sizeof(buf)) { |
|---|
| 73 | + error = len < 0 ? len : -EIO; |
|---|
| 74 | + dev_err(&eeti->client->dev, |
|---|
| 75 | + "failed to read touchscreen data: %d\n", |
|---|
| 76 | + error); |
|---|
| 77 | + return error; |
|---|
| 78 | + } |
|---|
| 79 | + |
|---|
| 80 | + /* Motion packet */ |
|---|
| 81 | + if (buf[0] & 0x80) |
|---|
| 82 | + eeti_ts_report_event(eeti, buf); |
|---|
| 83 | + |
|---|
| 84 | + return 0; |
|---|
| 85 | +} |
|---|
| 86 | + |
|---|
| 78 | 87 | static irqreturn_t eeti_ts_isr(int irq, void *dev_id) |
|---|
| 79 | 88 | { |
|---|
| 80 | 89 | struct eeti_ts *eeti = dev_id; |
|---|
| 81 | | - int len; |
|---|
| 82 | 90 | int error; |
|---|
| 83 | | - char buf[6]; |
|---|
| 91 | + |
|---|
| 92 | + mutex_lock(&eeti->mutex); |
|---|
| 84 | 93 | |
|---|
| 85 | 94 | do { |
|---|
| 86 | | - len = i2c_master_recv(eeti->client, buf, sizeof(buf)); |
|---|
| 87 | | - if (len != sizeof(buf)) { |
|---|
| 88 | | - error = len < 0 ? len : -EIO; |
|---|
| 89 | | - dev_err(&eeti->client->dev, |
|---|
| 90 | | - "failed to read touchscreen data: %d\n", |
|---|
| 91 | | - error); |
|---|
| 95 | + /* |
|---|
| 96 | + * If we have attention GPIO, trust it. Otherwise we'll read |
|---|
| 97 | + * once and exit. We assume that in this case we are using |
|---|
| 98 | + * level triggered interrupt and it will get raised again |
|---|
| 99 | + * if/when there is more data. |
|---|
| 100 | + */ |
|---|
| 101 | + if (eeti->attn_gpio && |
|---|
| 102 | + !gpiod_get_value_cansleep(eeti->attn_gpio)) { |
|---|
| 92 | 103 | break; |
|---|
| 93 | 104 | } |
|---|
| 94 | 105 | |
|---|
| 95 | | - if (buf[0] & 0x80) { |
|---|
| 96 | | - /* Motion packet */ |
|---|
| 97 | | - eeti_ts_report_event(eeti, buf); |
|---|
| 98 | | - } |
|---|
| 99 | | - } while (eeti->running && |
|---|
| 100 | | - eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio)); |
|---|
| 106 | + error = eeti_ts_read(eeti); |
|---|
| 107 | + if (error) |
|---|
| 108 | + break; |
|---|
| 101 | 109 | |
|---|
| 110 | + } while (eeti->running && eeti->attn_gpio); |
|---|
| 111 | + |
|---|
| 112 | + mutex_unlock(&eeti->mutex); |
|---|
| 102 | 113 | return IRQ_HANDLED; |
|---|
| 103 | 114 | } |
|---|
| 104 | 115 | |
|---|
| 105 | 116 | static void eeti_ts_start(struct eeti_ts *eeti) |
|---|
| 106 | 117 | { |
|---|
| 118 | + mutex_lock(&eeti->mutex); |
|---|
| 119 | + |
|---|
| 107 | 120 | eeti->running = true; |
|---|
| 108 | | - wmb(); |
|---|
| 109 | 121 | enable_irq(eeti->client->irq); |
|---|
| 122 | + |
|---|
| 123 | + /* |
|---|
| 124 | + * Kick the controller in case we are using edge interrupt and |
|---|
| 125 | + * we missed our edge while interrupt was disabled. We expect |
|---|
| 126 | + * the attention GPIO to be wired in this case. |
|---|
| 127 | + */ |
|---|
| 128 | + if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio)) |
|---|
| 129 | + eeti_ts_read(eeti); |
|---|
| 130 | + |
|---|
| 131 | + mutex_unlock(&eeti->mutex); |
|---|
| 110 | 132 | } |
|---|
| 111 | 133 | |
|---|
| 112 | 134 | static void eeti_ts_stop(struct eeti_ts *eeti) |
|---|
| 113 | 135 | { |
|---|
| 136 | + /* |
|---|
| 137 | + * Not locking here, just setting a flag and expect that the |
|---|
| 138 | + * interrupt thread will notice the flag eventually. |
|---|
| 139 | + */ |
|---|
| 114 | 140 | eeti->running = false; |
|---|
| 115 | 141 | wmb(); |
|---|
| 116 | 142 | disable_irq(eeti->client->irq); |
|---|
| .. | .. |
|---|
| 153 | 179 | return -ENOMEM; |
|---|
| 154 | 180 | } |
|---|
| 155 | 181 | |
|---|
| 182 | + mutex_init(&eeti->mutex); |
|---|
| 183 | + |
|---|
| 156 | 184 | input = devm_input_allocate_device(dev); |
|---|
| 157 | 185 | if (!input) { |
|---|
| 158 | 186 | dev_err(dev, "Failed to allocate input device.\n"); |
|---|