* [PATCH v2 1/2] Input: ads7846 - restore half-duplex support
2026-04-19 16:18 [PATCH v2 0/2] Input/omap1: fix touchscreen functionality on Nokia 770 Aaro Koskinen
@ 2026-04-19 16:18 ` Aaro Koskinen
2026-04-20 0:13 ` Dmitry Torokhov
2026-04-19 16:18 ` [PATCH v2 2/2] Input: ads7846 - fix up the pendown GPIO setup on Nokia 770 Aaro Koskinen
2026-04-19 20:10 ` [PATCH v2 0/2] Input/omap1: fix touchscreen functionality " Linus Walleij
2 siblings, 1 reply; 8+ messages in thread
From: Aaro Koskinen @ 2026-04-19 16:18 UTC (permalink / raw)
To: Dmitry Torokhov, Oleksij Rempel, Janusz Krzysztofik,
Tony Lindgren, Linus Walleij, linux-input
Cc: linux-kernel, linux-omap, Aaro Koskinen
On some boards, the SPI controller is limited to half-duplex and the driver
fails spamming "ads7846 spi2.1: spi_sync --> -22". Restore half-duplex
support with multiple SPI transfers.
Fixes: 9c9509717b53 ("Input: ads7846 - convert to full duplex")
Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
---
drivers/input/touchscreen/ads7846.c | 168 +++++++++++++++++++++++++++-
1 file changed, 166 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 0963b1a78a0c..4f8cc450e779 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -134,6 +134,9 @@ struct ads7846 {
bool disabled; /* P: lock */
bool suspended; /* P: lock */
+ int (*setup_spi_msg)(struct ads7846 *ts,
+ const struct ads7846_platform_data *pdata);
+ void (*read_state)(struct ads7846 *ts);
int (*filter)(void *data, int data_idx, int *val);
void *filter_data;
int (*get_pendown_state)(void);
@@ -797,6 +800,22 @@ static int ads7846_filter(struct ads7846 *ts)
return 0;
}
+static int ads7846_filter_one(struct ads7846 *ts, unsigned int cmd_idx)
+{
+ struct ads7846_packet *packet = ts->packet;
+ struct ads7846_buf_layout *l = &packet->l[cmd_idx];
+ int action, val;
+
+ val = ads7846_get_value(&packet->rx[l->offset + l->count - 1]);
+ action = ts->filter(ts->filter_data, cmd_idx, &val);
+ if (action == ADS7846_FILTER_REPEAT)
+ return -EAGAIN;
+ else if (action != ADS7846_FILTER_OK)
+ return -EIO;
+ ads7846_set_cmd_val(ts, cmd_idx, val);
+ return 0;
+}
+
static void ads7846_wait_for_hsync(struct ads7846 *ts)
{
if (ts->wait_for_sync) {
@@ -819,6 +838,45 @@ static void ads7846_wait_for_hsync(struct ads7846 *ts)
cpu_relax();
}
+static void ads7846_halfd_read_state(struct ads7846 *ts)
+{
+ struct ads7846_packet *packet = ts->packet;
+ int msg_idx = 0;
+
+ packet->ignore = false;
+
+ while (msg_idx < ts->msg_count) {
+ int error;
+
+ ads7846_wait_for_hsync(ts);
+
+ error = spi_sync(ts->spi, &ts->msg[msg_idx]);
+ if (error) {
+ dev_err_ratelimited(&ts->spi->dev, "spi_sync --> %d\n",
+ error);
+ packet->ignore = true;
+ return;
+ }
+
+ /*
+ * Last message is power down request, no need to convert
+ * or filter the value.
+ */
+ if (msg_idx == ts->msg_count - 1)
+ break;
+
+ error = ads7846_filter_one(ts, msg_idx);
+ if (error == -EAGAIN) {
+ continue;
+ } else if (error) {
+ packet->ignore = true;
+ msg_idx = ts->msg_count - 1;
+ } else {
+ msg_idx++;
+ }
+ }
+}
+
static void ads7846_read_state(struct ads7846 *ts)
{
struct ads7846_packet *packet = ts->packet;
@@ -947,7 +1005,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
while (!ts->stopped && get_pendown_state(ts)) {
/* pen is down, continue with the measurement */
- ads7846_read_state(ts);
+ ts->read_state(ts);
if (!ts->stopped)
ads7846_report_state(ts);
@@ -1034,6 +1092,102 @@ static int ads7846_setup_pendown(struct spi_device *spi,
return 0;
}
+/*
+ * Set up the transfers to read touchscreen state; this assumes we
+ * use formula #2 for pressure, not #3.
+ */
+static int ads7846_halfd_spi_msg(struct ads7846 *ts,
+ const struct ads7846_platform_data *pdata)
+{
+ struct spi_message *m = ts->msg;
+ struct spi_transfer *x = ts->xfer;
+ struct ads7846_packet *packet = ts->packet;
+ int vref = pdata->keep_vref_on;
+ unsigned int offset = 0;
+ unsigned int cmd_idx, b;
+ size_t size = 0;
+
+ if (pdata->settle_delay_usecs)
+ packet->count = 2;
+ else
+ packet->count = 1;
+
+ if (ts->model == 7846)
+ packet->cmds = 5; /* x, y, z1, z2, pwdown */
+ else
+ packet->cmds = 3; /* x, y, pwdown */
+
+ for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) {
+ struct ads7846_buf_layout *l = &packet->l[cmd_idx];
+ unsigned int max_count;
+
+ if (cmd_idx == packet->cmds - 1) {
+ cmd_idx = ADS7846_PWDOWN;
+ max_count = 1;
+ } else {
+ max_count = packet->count;
+ }
+
+ l->offset = offset;
+ offset += max_count;
+ l->count = max_count;
+ l->skip = 0;
+ size += sizeof(*packet->rx) * max_count;
+ }
+
+ /* We use two transfers per command. */
+ if (ARRAY_SIZE(ts->xfer) < offset * 2)
+ return -ENOMEM;
+
+ packet->rx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL);
+ if (!packet->rx)
+ return -ENOMEM;
+
+ if (ts->model == 7873) {
+ /*
+ * The AD7873 is almost identical to the ADS7846
+ * keep VREF off during differential/ratiometric
+ * conversion modes.
+ */
+ ts->model = 7846;
+ vref = 0;
+ }
+
+ ts->msg_count = 0;
+
+ for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) {
+ struct ads7846_buf_layout *l = &packet->l[cmd_idx];
+ u8 cmd;
+
+ ts->msg_count++;
+ spi_message_init(m);
+ m->context = ts;
+
+ if (cmd_idx == packet->cmds - 1)
+ cmd_idx = ADS7846_PWDOWN;
+
+ cmd = ads7846_get_cmd(cmd_idx, vref);
+
+ for (b = 0; b < l->count; b++) {
+ packet->rx[l->offset + b].cmd = cmd;
+ x->tx_buf = &packet->rx[l->offset + b].cmd;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+ x++;
+ x->rx_buf = &packet->rx[l->offset + b].data;
+ x->len = 2;
+ if (b < l->count - 1 && l->count > 1) {
+ x->delay.value = pdata->settle_delay_usecs;
+ x->delay.unit = SPI_DELAY_UNIT_USECS;
+ }
+ spi_message_add_tail(x, m);
+ x++;
+ }
+ m++;
+ }
+ return 0;
+}
+
/*
* Set up the transfers to read touchscreen state; this assumes we
* use formula #2 for pressure, not #3.
@@ -1248,6 +1402,14 @@ static int ads7846_probe(struct spi_device *spi)
if (!ts)
return -ENOMEM;
+ if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) {
+ ts->setup_spi_msg = ads7846_halfd_spi_msg;
+ ts->read_state = ads7846_halfd_read_state;
+ } else {
+ ts->setup_spi_msg = ads7846_setup_spi_msg;
+ ts->read_state = ads7846_read_state;
+ }
+
packet = devm_kzalloc(dev, sizeof(struct ads7846_packet), GFP_KERNEL);
if (!packet)
return -ENOMEM;
@@ -1342,7 +1504,9 @@ static int ads7846_probe(struct spi_device *spi)
ts->core_prop.swap_x_y = true;
}
- ads7846_setup_spi_msg(ts, pdata);
+ err = ts->setup_spi_msg(ts, pdata);
+ if (err)
+ return err;
ts->reg = devm_regulator_get(dev, "vcc");
if (IS_ERR(ts->reg)) {
--
2.39.2
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 2/2] Input: ads7846 - fix up the pendown GPIO setup on Nokia 770
2026-04-19 16:18 [PATCH v2 0/2] Input/omap1: fix touchscreen functionality on Nokia 770 Aaro Koskinen
2026-04-19 16:18 ` [PATCH v2 1/2] Input: ads7846 - restore half-duplex support Aaro Koskinen
@ 2026-04-19 16:18 ` Aaro Koskinen
2026-04-20 0:06 ` Dmitry Torokhov
2026-04-19 20:10 ` [PATCH v2 0/2] Input/omap1: fix touchscreen functionality " Linus Walleij
2 siblings, 1 reply; 8+ messages in thread
From: Aaro Koskinen @ 2026-04-19 16:18 UTC (permalink / raw)
To: Dmitry Torokhov, Oleksij Rempel, Janusz Krzysztofik,
Tony Lindgren, Linus Walleij, linux-input
Cc: linux-kernel, linux-omap, Aaro Koskinen
Commit 767d83361aaa6 ("Input: ads7846 - Convert to use software nodes")
added gpiod set up for the IRQ in the 770 board file, then another in
the touchscreen driver for reading the pen state. This will make the probe
fail:
[ 1.347381] ads7846 spi2.0: failed to request pendown GPIO
[ 1.361846] ads7846: probe of spi2.0 failed with error -16
I originally tried to fix it using non-exclusive flag, but that was not
found acceptable. Instead, just use a single gpiod.
Fixes: 767d83361aaa6 ("Input: ads7846 - Convert to use software nodes")
Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
---
arch/arm/mach-omap1/board-nokia770.c | 11 -----------
drivers/input/touchscreen/ads7846.c | 12 +++++++-----
2 files changed, 7 insertions(+), 16 deletions(-)
diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
index a5bf5554800f..8b8013ab4590 100644
--- a/arch/arm/mach-omap1/board-nokia770.c
+++ b/arch/arm/mach-omap1/board-nokia770.c
@@ -285,9 +285,6 @@ static void __init nokia770_cbus_init(void)
static struct gpiod_lookup_table nokia770_irq_gpio_table = {
.dev_id = NULL,
.table = {
- /* GPIO used by SPI device 1 */
- GPIO_LOOKUP("gpio-0-15", 15, "ads7846_irq",
- GPIO_ACTIVE_HIGH),
/* GPIO used for retu IRQ */
GPIO_LOOKUP("gpio-48-63", 14, "retu_irq",
GPIO_ACTIVE_HIGH),
@@ -307,8 +304,6 @@ static struct gpiod_lookup_table nokia770_irq_gpio_table = {
static void __init omap_nokia770_init(void)
{
- struct gpio_desc *d;
-
/* On Nokia 770, the SleepX signal is masked with an
* MPUIO line by default. It has to be unmasked for it
* to become functional */
@@ -322,12 +317,6 @@ static void __init omap_nokia770_init(void)
platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices));
gpiod_add_lookup_table(&nokia770_irq_gpio_table);
- d = gpiod_get(NULL, "ads7846_irq", GPIOD_IN);
- if (IS_ERR(d))
- pr_err("Unable to get ADS7846 IRQ GPIO descriptor\n");
- else
- nokia770_spi_board_info[1].irq = gpiod_to_irq(d);
-
spi_register_board_info(nokia770_spi_board_info,
ARRAY_SIZE(nokia770_spi_board_info));
omap_serial_init();
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 4f8cc450e779..ca7dbd3afe29 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -1084,6 +1084,8 @@ static int ads7846_setup_pendown(struct spi_device *spi,
dev_err(&spi->dev, "failed to request pendown GPIO\n");
return PTR_ERR(ts->gpio_pendown);
}
+ if (!spi->irq)
+ spi->irq = gpiod_to_irq(ts->gpio_pendown);
if (pdata->gpio_pendown_debounce)
gpiod_set_debounce(ts->gpio_pendown,
pdata->gpio_pendown_debounce);
@@ -1374,11 +1376,6 @@ static int ads7846_probe(struct spi_device *spi)
unsigned long irq_flags;
int err;
- if (!spi->irq) {
- dev_dbg(dev, "no IRQ?\n");
- return -EINVAL;
- }
-
/* don't exceed max specified sample rate */
if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
dev_err(dev, "f(sample) %d KHz?\n",
@@ -1455,6 +1452,11 @@ static int ads7846_probe(struct spi_device *spi)
if (err)
return err;
+ if (!spi->irq) {
+ dev_dbg(dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
if (pdata->penirq_recheck_delay_usecs)
ts->penirq_recheck_delay_usecs =
pdata->penirq_recheck_delay_usecs;
--
2.39.2
^ permalink raw reply related [flat|nested] 8+ messages in thread