This is an automated email from the ASF dual-hosted git repository.

jerpelea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 1e06783d83899d127a1d966a15defccd3ead472e
Author: Filipe Cavalcanti <[email protected]>
AuthorDate: Mon Aug 3 11:30:08 2026 -0300

    drivers/input: support ST7123 touchscreen controller
    
    Add support for ST7123 touchscreen controller (I2C only).
    It requires board level init to register a callback, as polling
    mode is not supported.
    
    Signed-off-by: Filipe Cavalcanti <[email protected]>
---
 drivers/input/CMakeLists.txt |    4 +
 drivers/input/Kconfig        |   24 +
 drivers/input/Make.defs      |    4 +
 drivers/input/st7123.c       | 1002 ++++++++++++++++++++++++++++++++++++++++++
 include/nuttx/input/st7123.h |  106 +++++
 5 files changed, 1140 insertions(+)

diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt
index 4527d2c9530..9891db6fa03 100644
--- a/drivers/input/CMakeLists.txt
+++ b/drivers/input/CMakeLists.txt
@@ -94,6 +94,10 @@ if(CONFIG_INPUT)
     list(APPEND SRCS gt9xx.c)
   endif()
 
+  if(CONFIG_INPUT_ST7123)
+    list(APPEND SRCS st7123.c)
+  endif()
+
   if(CONFIG_INPUT_BUTTONS)
     list(APPEND SRCS button_upper.c)
 
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 59d2d989088..56948b3e234 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -166,6 +166,30 @@ config UINPUT_KEYBOARD_BUFNUMBER
 
 endif
 
+config INPUT_ST7123
+       bool "ST7123 Touch Screen Controller"
+       default n
+       select I2C
+       select INPUT_TOUCHSCREEN
+       ---help---
+               Enable support for the ST7123 touchscreen controller.
+
+if INPUT_ST7123
+
+config INPUT_ST7123_I2C_FREQUENCY
+       int "I2C frequency"
+       default 400000
+       ---help---
+               Select the I2C frequency for the ST7123 touchscreen controller.
+
+config INPUT_ST7123_I2C_ADDRESS
+       hex "I2C address"
+       default 0x55
+       ---help---
+               Select the I2C address for the ST7123 touchscreen controller.
+
+endif # INPUT_ST7123
+
 config INPUT_MAX11802
        bool "MAX11802 touchscreen controller"
        default n
diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs
index e5408655a97..c7c22a56e25 100644
--- a/drivers/input/Make.defs
+++ b/drivers/input/Make.defs
@@ -143,6 +143,10 @@ ifeq ($(CONFIG_INPUT_MPR121_KEYPAD),y)
   CSRCS += mpr121.c
 endif
 
+ifeq ($(CONFIG_INPUT_ST7123),y)
+  CSRCS += st7123.c
+endif
+
 # Include input device driver build support
 
 DEPPATH += --dep-path input
diff --git a/drivers/input/st7123.c b/drivers/input/st7123.c
new file mode 100644
index 00000000000..25b1cefc9dc
--- /dev/null
+++ b/drivers/input/st7123.c
@@ -0,0 +1,1002 @@
+/****************************************************************************
+ * drivers/input/st7123.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <nuttx/bits.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/input/touchscreen.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/sched.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/input/st7123.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ST7123_TOUCH_FW_VERSION         0x00
+#define ST7123_TOUCH_STATUS             0x01
+#define ST7123_TOUCH_DEV_CTRL           0x02
+
+/* XY Coordinate resolution, Maximum Number of Touches Register */
+
+#define ST7123_TOUCH_MAX_X_COORD_H      0x05
+#define ST7123_TOUCH_MAX_X_COORD_L      0x06
+#define ST7123_TOUCH_MAX_Y_COORD_H      0x07
+#define ST7123_TOUCH_MAX_Y_COORD_L      0x08
+#define ST7123_TOUCH_MAX_TOUCHES        0x09
+#define ST7123_TOUCH_SENSING_COUNTER_H  0x0a
+#define ST7123_TOUCH_SENSING_COUNTER_L  0x0b
+#define ST7123_TOUCH_FW_REV_3           0x0c
+#define ST7123_TOUCH_FW_REV_2           0x0d
+#define ST7123_TOUCH_FW_REV_1           0x0e
+#define ST7123_TOUCH_FW_REV_0           0x0f
+#define ST7123_TOUCH_ADV_TOUCH_INFO     0x10
+#define ST7123_TOUCH_GESTURE_INFO       0x12
+#define ST7123_TOUCH_KEYS               0x13
+#define ST7123_TOUCH_MISC_INFO          0xf0
+#define ST7123_TOUCH_MISC_CTRL          0xf1
+
+/* Touch area registers */
+
+#define ST7123_TOUCH_AREA_SIZE         7    /* There are 7 registers for each 
touch area */
+#define ST7123_TOUCH_DATA_START        0x14
+
+/* Maximum number of touch areas this driver is able to report.  The value
+ * read back from ST7123_TOUCH_MAX_TOUCHES is clamped to this limit so that a
+ * bogus register value can never overrun the frame or sample buffers.
+ */
+
+#define ST7123_MAX_TOUCH_AREAS         10
+
+/* Registers 0x10 through 0x13 precede the per-area data at 0x14 and are read
+ * as part of the same touch frame.
+ */
+
+#define ST7123_FRAME_HEADER_SIZE \
+  (ST7123_TOUCH_DATA_START - ST7123_TOUCH_ADV_TOUCH_INFO)
+
+/* Advanced Touch Info Masks */
+
+#define ST7123_ADV_TOUCH_WITH_PROX     BIT(2)
+#define ST7123_ADV_TOUCH_WITH_COORD    BIT(3)
+#define ST7123_ADV_TOUCH_RST_CHIP      BIT(7)
+#define ST7123_ADV_TOUCH_PROX_STATUS   0x70
+
+/* Devices status obtained from STATUS register bits [3:0]
+ * and error codes obtained from STATUS register bits [7:4].
+ */
+
+#define ST7123_STATUS_MASK 0x0f
+#define ST7123_ERROR_MASK 0xf0
+#define ST7123_ERROR_SHIFT 4
+
+/* Miscellaneous information register */
+
+#define ST7123_MISC_SUPPORT_COORD_CHKSUM     BIT(4)
+#define ST7123_MISC_SUPPORT_PROXIMITY        BIT(5)
+#define ST7123_MISC_SUPPORT_SMART_WAKEUP_EN  BIT(7)
+
+/* Miscellaneous control register */
+
+#define ST7123_MISC_CTRL_SMART_WAKEUP_EN_BIT BIT(7)
+
+/* Device control register valid bits. All other bits must be set to 0. */
+
+#define ST7123_DEVICE_CTRL_RESET_BIT         BIT(0)
+#define ST7123_DEVICE_CTRL_POWER_DOWN_BIT    BIT(1)
+#define ST7123_DEVICE_CTRL_PROXIMITY_EN_BIT  BIT(5)
+
+/* Gesture codes */
+
+#define ST7123_GESTURE_NONE             0x00
+#define ST7123_GESTURE_DET_FAILED       0xff
+#define ST7123_GESTURE_DOUBLE_TAP       0xb0
+#define ST7123_GESTURE_SINGLE_TAP       0xb1
+#define ST7123_GESTURE_LONG_PRESS       0xb2
+#define ST7123_GESTURE_SWIPE_RIGHT      0xc0
+#define ST7123_GESTURE_SWIPE_LEFT       0xc1
+#define ST7123_GESTURE_SWIPE_DOWN       0xc2
+#define ST7123_GESTURE_SWIPE_UP         0xc3
+#define ST7123_GESTURE_ARROW_TOP        0xc4
+#define ST7123_GESTURE_ARROW_RIGHT      0xc5
+#define ST7123_GESTURE_ARROW_BOTTOM     0xc6
+#define ST7123_GESTURE_ARROW_LEFT       0xc7
+#define ST7123_GESTURE_TWO_FINGER_DOWN  0xc8
+
+#define ST7123_I2C_ADDRLEN        7
+
+/* Driver registration */
+
+#define DEV_FORMAT     "/dev/input%d"
+#define DEV_NAMELEN    16
+
+/* Startup */
+
+#define WAIT_TIMEOUT_STEP_US 100
+#define WAIT_TIMEOUT_MAX_US (WAIT_TIMEOUT_STEP_US * 1000)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Raw area data with proper bit field for software parsing */
+
+begin_packed_struct struct st7123_area_report_t
+{
+  uint8_t x_coord_h: 6;
+  uint8_t reserved_s: 1;
+  uint8_t valid: 1;
+  uint8_t x_coord_l;
+  uint8_t y_coord_h;
+  uint8_t y_coord_l;
+  uint8_t area_size;
+  uint8_t intensity;
+  uint8_t reserved_e;
+} end_packed_struct;
+
+/* Firmware revision from registers 0x0c through 0x0f */
+
+begin_packed_struct struct st7123_fw_revision_reg_t
+{
+  uint8_t fw_rev_3;
+  uint8_t fw_rev_2;
+  uint8_t fw_rev_1;
+  uint8_t fw_rev_0;
+} end_packed_struct;
+
+/* Advanced touch information from register 0x10 */
+
+begin_packed_struct struct st7123_adv_touch_info_reg_t
+{
+  uint8_t reserved: 2;
+  uint8_t with_prox: 1;
+  uint8_t with_coord: 1;
+  uint8_t prox_status: 3;
+  uint8_t rst_chip: 1;
+} end_packed_struct;
+
+/* Miscellaneous information from register 0xf0 */
+
+begin_packed_struct struct st7123_misc_info_reg_t
+{
+  uint8_t reserved_0: 3;
+  uint8_t support_coord_chksum: 1;
+  uint8_t support_proximity: 1;
+  uint8_t reserved_1: 1;
+  uint8_t support_smart_wakeup_en: 1;
+} end_packed_struct;
+
+/* Device capabilities from registers 0x05 through 0x09 */
+
+begin_packed_struct struct st7123_device_caps_reg_t
+{
+  uint8_t max_x_coord_h: 5;
+  uint8_t reserved_x: 2;
+  uint8_t max_x_coord_l;
+  uint8_t max_y_coord_h: 5;
+  uint8_t reserved_y: 2;
+  uint8_t max_y_coord_l;
+  uint8_t max_touches;
+} end_packed_struct;
+
+/* A complete touch report, registers 0x10 upwards. The controller keeps the
+ * interrupt line asserted until the whole frame has been consumed, so it
+ * must be fetched in a single transaction.
+ */
+
+begin_packed_struct struct st7123_frame_t
+{
+  struct st7123_adv_touch_info_reg_t adv_touch_info;
+  uint8_t reserved;
+  uint8_t gesture;
+  uint8_t keys;
+  struct st7123_area_report_t areas[ST7123_MAX_TOUCH_AREAS];
+} end_packed_struct;
+
+/* Status codes */
+
+typedef enum st7123_status_e
+{
+  ST7123_STATUS_NORMAL = 0,
+  ST7123_STATUS_INIT = 1,
+  ST7123_STATUS_ERROR = 2,
+  ST7123_STATUS_SMART_WAKEUP = 3,
+  ST7123_STATUS_IDLE = 4,
+  ST7123_STATUS_POWER_DOWN = 5,
+  ST7123_STATUS_PRE_SMART_WAKEUP = 6,
+  ST7123_STATUS_PROXIMITY = 7,
+  ST7123_STATUS_TRGT_PROXIMITY = 8,
+} st7123_status_t;
+
+/* Error codes */
+
+typedef enum st7123_error_e
+{
+  ST7123_ERROR_NO_ERROR = 0,
+  ST7123_ERROR_INVALID_ADDR = 1,
+  ST7123_ERROR_INVALID_VALUE = 2,
+  ST7123_ERROR_INVALID_PLATFORM = 3,
+  ST7123_ERROR_DEV_NOT_FOUND = 4,
+  ST7123_ERROR_DEV_STACK_OVERFLOW = 5,
+  ST7123_ERROR_DEV_INVALID_FW_TABLE = 6,
+} st7123_error_t;
+
+/* Main device structure */
+
+struct st7123_dev_t
+{
+  FAR struct touch_lowerhalf_s lower;
+  FAR const struct st7123_config_s *config;
+  struct i2c_master_s *i2c;
+  struct i2c_config_s i2c_config;
+  struct work_s work;
+  mutex_t lock;
+
+  struct st7123_frame_t frame;
+  struct st7123_adv_touch_info_reg_t adv_touch_info;
+  struct st7123_device_caps_reg_t device_caps;
+  struct st7123_fw_revision_reg_t fw_revision;
+  struct st7123_misc_info_reg_t misc_info;
+  uint8_t fw_version;
+
+  /* Contacts reported as down by the previous frame, one bit per area.  Used
+   * to turn the per-frame valid bits into DOWN/MOVE/UP transitions.
+   */
+
+  uint16_t downmap;
+
+  /* Last known position of each contact, so that the release event can be
+   * reported at the position where the contact was lost.
+   */
+
+  int16_t lastx[ST7123_MAX_TOUCH_AREAS];
+  int16_t lasty[ST7123_MAX_TOUCH_AREAS];
+
+  /* Scratch buffer for touch_event() samples. */
+
+  uint8_t sample_buf[SIZEOF_TOUCH_SAMPLE_S(ST7123_MAX_TOUCH_AREAS)];
+};
+
+/****************************************************************************
+ * Static Function Prototypes
+ ****************************************************************************/
+
+static int st7123_open(struct touch_lowerhalf_s *lower);
+static int st7123_close(struct touch_lowerhalf_s *lower);
+static int st7123_control(struct touch_lowerhalf_s *lower, int cmd,
+                          unsigned long arg);
+static int st7123_write(struct touch_lowerhalf_s *lower,
+                        FAR const char *buffer, size_t buflen);
+static int st7123_read_reg(struct st7123_dev_t *dev, uint8_t reg,
+                           uint8_t *value);
+static int st7123_write_reg(struct st7123_dev_t *dev, uint8_t reg,
+                            uint8_t value);
+static int st7123_read_sequential(struct st7123_dev_t *dev, uint8_t reg,
+                                  uint8_t *value, size_t count);
+static int st7123_read_status(struct st7123_dev_t *dev,
+                              FAR st7123_status_t *status);
+static int st7123_read_error(struct st7123_dev_t *dev,
+                             FAR st7123_error_t *error);
+static int st7123_probe_device(struct st7123_dev_t *dev);
+static void st7123_data_worker(FAR void *arg);
+static int st7123_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/****************************************************************************
+ * Static Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: st7123_control
+ *
+ * Description:
+ *   Handle device-specific ioctl commands for the ST7123 lower half.
+ *   No commands are currently supported.
+ *
+ * Input Parameters:
+ *   lower - Touchscreen lower-half state
+ *   cmd   - ioctl command
+ *   arg   - ioctl argument
+ *
+ * Returned Value:
+ *   -ENOTTY is always returned.
+ *
+ ****************************************************************************/
+
+static int st7123_control(struct touch_lowerhalf_s *lower, int cmd,
+                          unsigned long arg)
+{
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: st7123_write
+ *
+ * Description:
+ *   Write to the ST7123 lower half.  Writes are not supported.
+ *
+ * Input Parameters:
+ *   lower  - Touchscreen lower-half state
+ *   buffer - Data to write
+ *   buflen - Number of bytes to write
+ *
+ * Returned Value:
+ *   -ENOTTY is always returned.
+ *
+ ****************************************************************************/
+
+static int st7123_write(struct touch_lowerhalf_s *lower,
+                        FAR const char *buffer, size_t buflen)
+{
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: st7123_open
+ *
+ * Description:
+ *   Bring the ST7123 out of power-down, optionally disable smart wakeup,
+ *   and wait until the device reports the NORMAL status.  Clears the
+ *   contact bitmap so the first frame after open does not emit stale
+ *   release events.
+ *
+ * Input Parameters:
+ *   lower - Touchscreen lower-half state
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_open(struct touch_lowerhalf_s *lower)
+{
+  DEBUGASSERT(lower != NULL);
+
+  struct st7123_dev_t *dev = (struct st7123_dev_t *) lower;
+  st7123_status_t status = ST7123_STATUS_INIT;
+  st7123_error_t error;
+  uint32_t timeout_cnt = 0;
+  uint8_t regval;
+  int ret;
+
+  /* Power up the and reset the device */
+
+  ret = st7123_write_reg(dev, ST7123_TOUCH_DEV_CTRL, 0x0);
+  if (ret != OK)
+    {
+      return ret;
+    }
+
+  /* Disable smart wakeup if available, for now */
+
+  if (dev->misc_info.support_smart_wakeup_en)
+    {
+      ret = st7123_read_reg(dev, ST7123_TOUCH_MISC_CTRL, &regval);
+      if (ret == OK)
+        {
+          regval &= ~ST7123_MISC_CTRL_SMART_WAKEUP_EN_BIT;
+          ret = st7123_write_reg(dev, ST7123_TOUCH_MISC_CTRL, regval);
+          if (ret != OK)
+            {
+              ierr("failed to disable smart wakeup: %d", ret);
+              return ret;
+            }
+        }
+    }
+
+  while (timeout_cnt < WAIT_TIMEOUT_MAX_US)
+    {
+      ret = st7123_read_status(dev, &status);
+      if (ret == OK && status == ST7123_STATUS_NORMAL)
+        {
+          break;
+        }
+
+      nxsched_usleep(WAIT_TIMEOUT_STEP_US);
+      timeout_cnt += WAIT_TIMEOUT_STEP_US;
+    }
+
+  if (status != ST7123_STATUS_NORMAL)
+    {
+      if (st7123_read_error(dev, &error) == OK)
+        {
+          ierr("%s open timeout. Status: %d, error: %d", __func__,
+               status, error);
+        }
+
+      return -EIO;
+    }
+
+  /* No contact is down on a freshly opened device */
+
+  dev->downmap = 0;
+  memset(&dev->lastx, 0, sizeof(dev->lastx));
+  memset(&dev->lasty, 0, sizeof(dev->lasty));
+
+  iinfo("opened");
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: st7123_close
+ *
+ * Description:
+ *   Power down the ST7123 and verify that the STATUS register reports
+ *   POWER_DOWN.
+ *
+ * Input Parameters:
+ *   lower - Touchscreen lower-half state
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_close(struct touch_lowerhalf_s *lower)
+{
+  DEBUGASSERT(lower != NULL);
+
+  struct st7123_dev_t *dev = (struct st7123_dev_t *) lower;
+  st7123_status_t status;
+  int ret;
+
+  /* Power down the device */
+
+  ret = st7123_write_reg(dev, ST7123_TOUCH_DEV_CTRL,
+                         ST7123_DEVICE_CTRL_POWER_DOWN_BIT);
+  if (ret != OK)
+    {
+      return ret;
+    }
+
+  nxsched_usleep(WAIT_TIMEOUT_STEP_US);
+
+  ret = st7123_read_status(dev, &status);
+  if (ret != OK)
+    {
+      return ret;
+    }
+
+  if (status != ST7123_STATUS_POWER_DOWN)
+    {
+      ierr("%s failed to power down. Status: %d", __func__, status);
+      return -EIO;
+    }
+
+  iinfo("%s powered down", __func__);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: st7123_read_reg
+ *
+ * Description:
+ *   Read a single ST7123 register over I2C.  The register address is
+ *   written as a two-byte Start Reg H / Start Reg L pair, then one byte
+ *   is read back.
+ *
+ * Input Parameters:
+ *   dev   - ST7123 device state
+ *   reg   - Register address to read
+ *   value - Location to receive the register value
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_read_reg(struct st7123_dev_t *dev, uint8_t reg,
+                           uint8_t *value)
+{
+  int ret;
+  uint8_t write_buffer[2];
+
+  write_buffer[0] = 0;
+  write_buffer[1] = reg;
+
+  nxmutex_lock(&dev->lock);
+  ret = i2c_writeread(dev->i2c, &dev->i2c_config, write_buffer, 2, value, 1);
+  nxmutex_unlock(&dev->lock);
+  if (ret != OK)
+    {
+      ierr("Failed to read register %02x: %d", reg, ret);
+    }
+  return ret;
+}
+
+/****************************************************************************
+ * Name: st7123_read_sequential
+ *
+ * Description:
+ *   Read a contiguous run of ST7123 registers over I2C, starting at the
+ *   given register address.  Used to fetch multi-byte structures such as
+ *   the firmware revision, device capabilities, and the full touch frame.
+ *
+ * Input Parameters:
+ *   dev   - ST7123 device state
+ *   reg   - First register address to read
+ *   value - Buffer to receive the register values
+ *   count - Number of bytes to read
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_read_sequential(struct st7123_dev_t *dev, uint8_t reg,
+                                  uint8_t *value, size_t count)
+{
+  int ret;
+  uint8_t write_buffer[2];
+
+  write_buffer[0] = 0;
+  write_buffer[1] = reg;
+
+  nxmutex_lock(&dev->lock);
+  ret = i2c_writeread(dev->i2c, &dev->i2c_config, write_buffer, 2,
+                      value, count);
+  nxmutex_unlock(&dev->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: st7123_write_reg
+ *
+ * Description:
+ *   Write a single ST7123 register over I2C.  The transfer carries the
+ *   two-byte Start Reg H / Start Reg L address followed by the value.
+ *
+ * Input Parameters:
+ *   dev   - ST7123 device state
+ *   reg   - Register address to write
+ *   value - Value to write
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_write_reg(struct st7123_dev_t *dev, uint8_t reg,
+                            uint8_t value)
+{
+  int ret;
+  uint8_t buffer[3];
+
+  buffer[0] = 0;
+  buffer[1] = reg;
+  buffer[2] = value;
+
+  nxmutex_lock(&dev->lock);
+  ret = i2c_write(dev->i2c, &dev->i2c_config, buffer, 3);
+  nxmutex_unlock(&dev->lock);
+  if (ret != OK)
+    {
+      ierr("Failed to write register %02x", reg);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: st7123_data_worker
+ *
+ * Description:
+ *   Work-queue handler that reads one touch frame from the ST7123 and
+ *   converts the per-area valid bits into DOWN / MOVE / UP events for the
+ *   touchscreen upper half.  Gesture codes from the controller are mapped
+ *   onto the NuttX touch gesture values where possible.  Frames that carry
+ *   no contact change are discarded.
+ *
+ * Input Parameters:
+ *   arg - Pointer to the ST7123 device state
+ *
+ ****************************************************************************/
+
+static void st7123_data_worker(FAR void *arg)
+{
+  FAR struct st7123_dev_t *dev = (FAR struct st7123_dev_t *)arg;
+  struct touch_lowerhalf_s *lower = &dev->lower;
+  FAR struct touch_sample_s *sample =
+    (FAR struct touch_sample_s *)dev->sample_buf;
+  FAR struct touch_point_s *point;
+  FAR struct st7123_area_report_t *area;
+  uint64_t timestamp;
+  uint16_t downmap = 0;
+  size_t framelen;
+  int npoints = 0;
+  int ret;
+  int i;
+
+  framelen = ST7123_FRAME_HEADER_SIZE +
+             lower->maxpoint * ST7123_TOUCH_AREA_SIZE;
+
+  ret = st7123_read_sequential(dev, ST7123_TOUCH_ADV_TOUCH_INFO,
+                               (uint8_t *) &dev->frame, framelen);
+  if (ret != OK)
+    {
+      ierr("failed to read touch frame: %d", ret);
+      return;
+    }
+
+  timestamp = touch_get_time();
+  memset(sample, 0, sizeof(dev->sample_buf));
+
+  for (i = 0; i < lower->maxpoint; i++)
+    {
+      bool was_down = (dev->downmap & (1 << i)) != 0;
+
+      area = &dev->frame.areas[i];
+
+      if (area->valid)
+        {
+          dev->lastx[i] = (area->x_coord_h << 8) | area->x_coord_l;
+          dev->lasty[i] = (area->y_coord_h << 8) | area->y_coord_l;
+          downmap |= 1 << i;
+        }
+      else if (!was_down)
+        {
+          /* The area is idle and was idle in the previous frame too, so
+           * there is nothing to report for it.
+           */
+
+          continue;
+        }
+
+      point            = &sample->point[npoints++];
+      point->id        = i;
+      point->x         = dev->lastx[i];
+      point->y         = dev->lasty[i];
+      point->timestamp = timestamp;
+
+      if (area->valid)
+        {
+          point->pressure = area->intensity;
+          switch (dev->frame.gesture)
+            {
+              case ST7123_GESTURE_DOUBLE_TAP:
+                point->gesture = TOUCH_DOUBLE_CLICK;
+                break;
+              case ST7123_GESTURE_SWIPE_UP:
+                point->gesture = TOUCH_SLIDE_UP;
+                break;
+              case ST7123_GESTURE_SWIPE_DOWN:
+                point->gesture = TOUCH_SLIDE_DOWN;
+                break;
+              case ST7123_GESTURE_SWIPE_LEFT:
+                point->gesture = TOUCH_SLIDE_LEFT;
+                break;
+              case ST7123_GESTURE_SWIPE_RIGHT:
+                point->gesture = TOUCH_SLIDE_RIGHT;
+                break;
+              default:
+                point->gesture = 0xff;
+                break;
+            }
+          point->flags    = (was_down ? TOUCH_MOVE : TOUCH_DOWN) |
+                            TOUCH_ID_VALID | TOUCH_POS_VALID |
+                            TOUCH_PRESSURE_VALID;
+        }
+      else
+        {
+          /* The contact was lost.  Report the release at the last known
+           * position so that consumers can act on it.
+           */
+
+          point->flags = TOUCH_UP | TOUCH_ID_VALID | TOUCH_POS_VALID;
+        }
+    }
+
+  dev->downmap = downmap;
+
+  /* An interrupt without any contact change carries no information */
+
+  if (npoints == 0)
+    {
+      return;
+    }
+
+  sample->npoints = npoints;
+  touch_event(lower->priv, sample);
+}
+
+/****************************************************************************
+ * Name: st7123_read_status
+ *
+ * Description:
+ *   Read the STATUS register and return the device status from bits [3:0].
+ *
+ * Input Parameters:
+ *   dev    - ST7123 device state
+ *   status - Location to receive the decoded status
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_read_status(struct st7123_dev_t *dev,
+                              FAR st7123_status_t *status)
+{
+  int ret;
+  uint8_t regval;
+
+  ret = st7123_read_reg(dev, ST7123_TOUCH_STATUS, &regval);
+  if (ret != OK)
+    {
+      ierr("failed to read status: %d", ret);
+      return ret;
+    }
+
+  *status = (st7123_status_t) (regval & ST7123_STATUS_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: st7123_read_error
+ *
+ * Description:
+ *   Read the STATUS register and return the error code from bits [7:4].
+ *
+ * Input Parameters:
+ *   dev   - ST7123 device state
+ *   error - Location to receive the decoded error code
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_read_error(struct st7123_dev_t *dev,
+                             FAR st7123_error_t *error)
+{
+  int ret;
+  uint8_t regval;
+
+  ret = st7123_read_reg(dev, ST7123_TOUCH_STATUS, &regval);
+  if (ret != OK)
+    {
+      ierr("failed to read error: %d", ret);
+      return ret;
+    }
+
+  *error = (st7123_error_t) ((regval & ST7123_ERROR_MASK) >>
+                             ST7123_ERROR_SHIFT);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: st7123_probe_device
+ *
+ * Description:
+ *   Read the firmware version, firmware revision, advanced touch info,
+ *   miscellaneous capabilities, and coordinate / touch-count limits from
+ *   the ST7123 and cache them in the device structure.
+ *
+ * Input Parameters:
+ *   dev - ST7123 device state
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+static int st7123_probe_device(struct st7123_dev_t *dev)
+{
+  int ret;
+
+  ret = st7123_read_reg(dev, ST7123_TOUCH_FW_VERSION, &dev->fw_version);
+  ret |= st7123_read_reg(dev, ST7123_TOUCH_ADV_TOUCH_INFO,
+                         (uint8_t *) &dev->adv_touch_info);
+  ret |= st7123_read_reg(dev, ST7123_TOUCH_MISC_INFO,
+                         (uint8_t *) &dev->misc_info);
+  ret |= st7123_read_sequential(dev, ST7123_TOUCH_FW_REV_3,
+                                (uint8_t *) &dev->fw_revision,
+                                sizeof(dev->fw_revision));
+  ret |= st7123_read_sequential(dev, ST7123_TOUCH_MAX_X_COORD_H,
+                                (uint8_t *) &dev->device_caps,
+                                sizeof(dev->device_caps));
+
+  if (ret != OK)
+    {
+      ierr("failed to read device capabilities: %d", ret);
+      return -EIO;
+  }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: st7123_interrupt
+ *
+ * Description:
+ *   GPIO interrupt handler attached by the board through config->attach().
+ *   Schedules st7123_data_worker() on the high-priority work queue.
+ *
+ * Input Parameters:
+ *   irq     - Interrupt number (unused)
+ *   context - Interrupt context (unused)
+ *   arg     - Pointer to the ST7123 device state
+ *
+ * Returned Value:
+ *   OK is always returned.
+ *
+ ****************************************************************************/
+
+static int st7123_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct st7123_dev_t *priv = (FAR struct st7123_dev_t *)arg;
+  int ret;
+
+  DEBUGASSERT(priv != NULL);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ret = work_queue(HPWORK, &priv->work, st7123_data_worker, priv, 0);
+  if (ret < 0)
+    {
+      ierr("ERROR: Failed to queue work: %d\n", ret);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: st7123_register
+ *
+ * Description:
+ *   Probe the ST7123 over I2C, configure the touchscreen lower half from
+ *   the reported resolution and touch-area count, register it as
+ *   /dev/inputN, and attach the board interrupt via config->attach().
+ *
+ * Input Parameters:
+ *   i2c    - I2C master used to talk to the ST7123
+ *   minor  - Device minor number used to form /dev/inputN
+ *   config - Persistent board configuration; config->attach must be valid
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+int st7123_register(FAR struct i2c_master_s *i2c, uint8_t minor,
+                    FAR const struct st7123_config_s *config)
+{
+  FAR struct st7123_dev_t *priv;
+  char devname[DEV_NAMELEN];
+  st7123_status_t status;
+  int ret;
+
+  DEBUGASSERT(i2c != NULL && config != NULL && config->attach != NULL);
+
+  if (config == NULL || config->attach == NULL)
+    {
+      ierr("ERROR: board config->attach is required\n");
+      return -EINVAL;
+    }
+
+  priv = kmm_zalloc(sizeof(struct st7123_dev_t));
+  if (priv == NULL)
+    {
+      ierr("ERROR: kmm_zalloc(%zu) failed\n", sizeof(struct st7123_dev_t));
+      return -ENOMEM;
+    }
+
+  priv->config = config;
+  priv->lower.control = st7123_control;
+  priv->lower.write   = st7123_write;
+  priv->lower.open    = st7123_open;
+  priv->lower.close   = st7123_close;
+
+  priv->i2c = i2c;
+  priv->i2c_config.frequency = CONFIG_INPUT_ST7123_I2C_FREQUENCY;
+  priv->i2c_config.address = CONFIG_INPUT_ST7123_I2C_ADDRESS;
+  priv->i2c_config.addrlen = ST7123_I2C_ADDRLEN;
+
+  nxmutex_init(&priv->lock);
+
+  ret = st7123_read_status(priv, &status);
+  if (ret != OK)
+    {
+      goto errout_with_lock;
+    }
+
+  if (status == ST7123_STATUS_POWER_DOWN)
+    {
+      st7123_write_reg(priv, ST7123_TOUCH_DEV_CTRL, 0x0);
+    }
+
+  ret = st7123_probe_device(priv);
+  if (ret != OK)
+    {
+      ierr("failed to probe st7123");
+      ret = -EINVAL;
+      goto errout_with_lock;
+    }
+
+  if (priv->device_caps.max_touches == 0)
+    {
+      ierr("device reports no touch areas");
+      ret = -ENODEV;
+      goto errout_with_lock;
+    }
+
+  priv->lower.maxpoint = priv->device_caps.max_touches;
+  priv->lower.xres = ((priv->device_caps.max_x_coord_h << 8) +
+                      priv->device_caps.max_x_coord_l);
+  priv->lower.yres = ((priv->device_caps.max_y_coord_h << 8) +
+                       priv->device_caps.max_y_coord_l);
+  priv->lower.flags = 0;
+
+  iinfo("probed: fw %u rev %u.%u.%u.%u", priv->fw_version,
+        priv->fw_revision.fw_rev_3, priv->fw_revision.fw_rev_2,
+        priv->fw_revision.fw_rev_1, priv->fw_revision.fw_rev_0);
+  iinfo("resolution: %d x %d, max touches: %u, smart wakeup: %u",
+        priv->lower.xres, priv->lower.yres, priv->lower.maxpoint,
+        priv->misc_info.support_smart_wakeup_en);
+
+  snprintf(devname, sizeof(devname), DEV_FORMAT, minor);
+  ret = touch_register(&priv->lower, devname, priv->lower.maxpoint);
+  if (ret < 0)
+    {
+      ierr("failed to register %s: %d", devname, ret);
+      goto errout_with_lock;
+    }
+
+  /* Attach after the upper half is ready so the ISR can safely queue work. */
+
+  ret = config->attach(config, st7123_interrupt, priv);
+  if (ret < 0)
+    {
+      ierr("ERROR: Failed to attach interrupt: %d\n", ret);
+      goto errout_with_register;
+    }
+
+  return OK;
+
+errout_with_register:
+  touch_unregister(&priv->lower, devname);
+
+errout_with_lock:
+  nxmutex_destroy(&priv->lock);
+  kmm_free(priv);
+  return ret;
+}
diff --git a/include/nuttx/input/st7123.h b/include/nuttx/input/st7123.h
new file mode 100644
index 00000000000..affe2b31612
--- /dev/null
+++ b/include/nuttx/input/st7123.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+ * include/nuttx/input/st7123.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* The ST7123 is an I2C capacitive touchscreen controller.  This header
+ * exposes the board-facing registration API used to bind the driver to an
+ * I2C bus and GPIO interrupt.  Once registered, the device appears as
+ * /dev/inputN and reports multi-touch samples through the standard
+ * touchscreen upper half.
+ */
+
+#ifndef __INCLUDE_NUTTX_INPUT_ST7123_H
+#define __INCLUDE_NUTTX_INPUT_ST7123_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/irq.h>
+
+#ifdef CONFIG_INPUT_ST7123
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Board-specific configuration.  The board must supply attach(), which
+ * wires the ST7123 INT pin to the given handler.  Memory for this structure
+ * is provided by the caller, is not copied by the driver, and must persist
+ * while the driver is active.
+ */
+
+struct st7123_config_s
+{
+  CODE int (*attach)(FAR const struct st7123_config_s *config,
+                     xcpt_t isr, FAR void *arg);
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: st7123_register
+ *
+ * Description:
+ *   Probe the ST7123 over I2C, configure the touchscreen lower half from
+ *   the reported resolution and touch-area count, register it as
+ *   /dev/inputN where N is the given minor number, and attach the board
+ *   interrupt via config->attach().
+ *
+ *   I2C frequency and slave address are taken from
+ *   CONFIG_INPUT_ST7123_I2C_FREQUENCY and CONFIG_INPUT_ST7123_I2C_ADDRESS.
+ *
+ * Input Parameters:
+ *   i2c    - I2C master used to talk to the ST7123
+ *   minor  - Device minor number used to form /dev/inputN
+ *   config - Persistent board configuration; config->attach must be valid
+ *
+ * Returned Value:
+ *   Zero (OK) on success.  Otherwise, a negated errno value is returned to
+ *   indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int st7123_register(FAR struct i2c_master_s *i2c, uint8_t minor,
+                    FAR const struct st7123_config_s *config);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_INPUT_ST7123 */
+#endif /* __INCLUDE_NUTTX_INPUT_ST7123_H */

Reply via email to