input/gt9xx: fix register write and no-contact read

Two defects keep the driver from reporting touches on a board that
cannot use the interrupt line.

The register write built two messages joined by I2C_M_NOSTART.  That
puts the same bytes on the wire as a single three byte message, but
resuming a transfer without a start condition is optional, and a
controller that does not implement it fails the transfer.  On the
ESP32-P4 every write returned -ETIMEDOUT, so the buffer status clear
at 0x814E never reached the controller and gt9xx_read_touch_data()
returned an error for every read.  Send the register address and the
value as a single message.

read() returned a full struct touch_sample_s even when the controller
reported no contact, with npoints set to zero.  A caller that judges
the read by its return value takes that for valid data: the LVGL
touchscreen driver reads a second sample to decide whether to keep
reading, always gets one, so it sets continue_reading on every pass
and lv_indev_read() never returns.  The display then stops refreshing
after the first frame while the touch reads spin.  Return -EAGAIN when
there is no contact and the file was opened with O_NONBLOCK, which is
what the touchscreen upper half does in the same situation.  A
blocking reader keeps the previous behaviour.

While here, add the blank line after the declaration in gt9xx_poll() that
nxstyle asks for.  It predates this change, but the CI runs checkpatch over
every file a commit touches, so it has to go.

Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
This commit is contained in:
Jorge Guzman 2026-08-31 01:23:30 -03:00 committed by Xiang Xiao
parent b496611b7f
commit e884b6c3c8

View file

@ -30,6 +30,7 @@
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
@ -37,6 +38,7 @@
#include <poll.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <nuttx/debug.h>
#include <nuttx/arch.h>
@ -237,40 +239,27 @@ static int gt9xx_i2c_write(FAR struct gt9xx_dev_s *dev,
{
int ret;
/* Send the Register Address, MSB first */
/* Send the Register Address MSB first, followed by the value, as a
* single message. Splitting it into two messages joined by
* I2C_M_NOSTART puts the same bytes on the wire, but not every I2C
* controller can resume a transfer that way.
*/
uint8_t regbuf[2] =
uint8_t buf[3] =
{
reg >> 8, /* First Byte: MSB */
reg & 0xff /* Second Byte: LSB */
reg >> 8, /* First Byte: Register Address MSB */
reg & 0xff, /* Second Byte: Register Address LSB */
val /* Third Byte: Value to be written */
};
/* Send the Register Value */
/* Compose the I2C Message */
uint8_t buf[1] =
{
val /* Value to be written */
};
/* Compose the I2C Messages */
struct i2c_msg_s msgv[2] =
struct i2c_msg_s msgv[1] =
{
{
/* Send the I2C Register Address */
.frequency = CONFIG_INPUT_GT9XX_I2C_FREQUENCY,
.addr = dev->addr,
.flags = 0,
.buffer = regbuf,
.length = sizeof(regbuf)
},
{
/* Send the I2C Register Value */
.frequency = CONFIG_INPUT_GT9XX_I2C_FREQUENCY,
.addr = dev->addr,
.flags = I2C_M_NOSTART,
.buffer = buf,
.length = sizeof(buf)
}
@ -551,9 +540,20 @@ static ssize_t gt9xx_read(FAR struct file *filep, FAR char *buffer,
iinfo("skip duplicate x=%d, y=%d\n", priv->x, priv->y);
}
/* Return the Touch Point */
/* Return the Touch Point, if there is one. With no contact and a
* non-blocking read there is nothing to hand over: a sample carrying
* zero points would look like valid data to callers that judge the
* read by its return value.
*/
memcpy(buffer, &sample, sizeof(sample));
if (sample.npoints == 0 && (filep->f_oflags & O_NONBLOCK) != 0)
{
ret = -EAGAIN;
}
else
{
memcpy(buffer, &sample, sizeof(sample));
}
/* Begin Critical Section */
@ -826,6 +826,7 @@ static int gt9xx_poll(FAR struct file *filep, FAR struct pollfd *fds,
/* If Poll Teardown: Remove the poll setup */
FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv;
DEBUGASSERT(slot != NULL);
*slot = NULL;