From 1738d60e8ce7b8cf5de9f8c8804b82a914b30a88 Mon Sep 17 00:00:00 2001 From: Filipe Cavalcanti Date: Mon, 3 Aug 2026 11:30:08 -0300 Subject: [PATCH 1/2] 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 --- 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(+) create mode 100644 drivers/input/st7123.c create mode 100644 include/nuttx/input/st7123.h diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt index 4527d2c953019..9891db6fa03dc 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 59d2d98908845..56948b3e2349e 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 e5408655a97fe..c7c22a56e2518 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 0000000000000..25b1cefc9dc27 --- /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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * 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, ®val); + 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, ®val); + 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, ®val); + 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 0000000000000..affe2b31612d8 --- /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 + +#include + +#include +#include + +#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 */ From e430a6f50eafb0d0fb5facfbfb4a6ee3b640aecf Mon Sep 17 00:00:00 2001 From: Filipe Cavalcanti Date: Mon, 3 Aug 2026 14:54:00 -0300 Subject: [PATCH 2/2] Documentation: add ST7123 to input docs Adds documentation to input and touchscreen controller files, regarding support for ST7123 IC. Signed-off-by: Filipe Cavalcanti --- .../drivers/character/input/index.rst | 1 + .../drivers/character/input/st7123.rst | 127 ++++++++++++++++++ .../drivers/character/touchscreen.rst | 11 +- 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 Documentation/components/drivers/character/input/st7123.rst diff --git a/Documentation/components/drivers/character/input/index.rst b/Documentation/components/drivers/character/input/index.rst index cc15022a11833..ab7f9161c683c 100644 --- a/Documentation/components/drivers/character/input/index.rst +++ b/Documentation/components/drivers/character/input/index.rst @@ -9,5 +9,6 @@ Input Devices keypad.rst mpr121.rst sbutton.rst + st7123.rst See ``include/nuttx/input/*.h`` for registration information. diff --git a/Documentation/components/drivers/character/input/st7123.rst b/Documentation/components/drivers/character/input/st7123.rst new file mode 100644 index 0000000000000..88678ac612442 --- /dev/null +++ b/Documentation/components/drivers/character/input/st7123.rst @@ -0,0 +1,127 @@ +============================== +ST7123 Capacitive Touchscreen +============================== + +**What is the ST7123**. The ST7123 is an I2C capacitive multi-touch +controller used on TDDI (Touch and Display Driver Integration) panels. +It reports up to ten simultaneous contacts, optional gesture codes, and +contact intensity. The same I2C register map is also used by related +parts such as the ST7121. + +**Purpose**. The ST7123 driver is a touchscreen lower-half that probes +the controller over I2C, reads complete touch frames on interrupt, and +delivers multi-touch samples through the common touchscreen upper-half. +Once registered, the device appears as ``/dev/inputN`` and applications +read ``struct touch_sample_s`` samples as described in +:doc:`../touchscreen`. + +**Driver Overview**. The board supplies a persistent +``struct st7123_config_s`` whose ``attach`` member wires the controller +INT pin to the driver interrupt handler. ``st7123_register()`` probes +the part, registers ``/dev/inputN``, then calls ``config->attach()`` with +the driver ISR and the allocated device instance as ``arg``. On each +falling edge of INT the ISR queues ``st7123_data_worker()`` on the +high-priority work queue. The worker fetches one full touch frame +(advanced-touch header plus every touch area) in a single I2C +transaction, converts per-area *valid* bits into ``TOUCH_DOWN`` / +``TOUCH_MOVE`` / ``TOUCH_UP`` transitions, and pushes the sample with +``touch_event()``. The upper half stores the sample in a circular +buffer for ``read()`` / ``poll()`` clients. + +**Configuration**. Enable the driver with: + +- ``CONFIG_INPUT=y`` +- ``CONFIG_INPUT_TOUCHSCREEN=y`` +- ``CONFIG_INPUT_ST7123=y`` +- ``CONFIG_SCHED_HPWORK=y`` (required; frame processing runs on HPWORK) +- ``CONFIG_INPUT_ST7123_I2C_FREQUENCY`` (default ``400000``) +- ``CONFIG_INPUT_ST7123_I2C_ADDRESS`` (default ``0x55``) + +**Board Support**. To support the ST7123 a board must provide: + +#. **I2C Bus** + + - An initialized ``struct i2c_master_s`` instance that can reach the + controller at ``CONFIG_INPUT_ST7123_I2C_ADDRESS``. + +#. **Board Configuration / Interrupt Attach** + + - A persistent ``struct st7123_config_s`` whose ``attach`` member + configures the INT GPIO (typically active-low / falling edge with + pull-up) and connects it to the given ``xcpt_t`` handler, passing + through the opaque ``arg`` provided by the driver. + - ``attach`` must remain valid for the lifetime of the driver; the + structure is not copied. + - Registration fails with ``-EINVAL`` if ``config`` or + ``config->attach`` is ``NULL``. + +#. **Registration Hook** + + - Call ``st7123_register(i2c, minor, &config)`` during board + bring-up. The driver attaches and may enable the interrupt only + after ``touch_register()`` succeeds, so an early edge cannot reach + an uninitialized device. + +Example board wiring: + +.. code-block:: c + + static int board_st7123_attach(FAR const struct st7123_config_s *config, + xcpt_t isr, FAR void *arg) + { + /* Configure the INT GPIO and attach isr(arg) to it */ + } + + static const struct st7123_config_s g_st7123_config = + { + .attach = board_st7123_attach, + }; + + int err = st7123_register(i2c, 0, &g_st7123_config); + +**Data Path Summary**. + +- Board obtains the I2C master and calls + ``st7123_register(i2c, 0, &g_st7123_config)`` +- ``st7123_register()`` allocates the device instance, probes firmware / + resolution / touch count, fills ``struct touch_lowerhalf_s``, and + calls ``touch_register(..., "/dev/input0", maxpoint)`` +- ``config->attach()`` wires the INT pin to the driver ISR with the + device instance as ``arg`` +- Each INT schedules ``st7123_data_worker()`` on HPWORK +- The worker reads the frame starting at register ``0x10`` and reports + contacts through ``touch_event()`` +- Applications open ``/dev/input0`` and read + ``struct touch_sample_s`` (sized with ``SIZEOF_TOUCH_SAMPLE_S(n)``) + +**Open / Close Behavior**. + +- ``open()`` powers the controller up (clears ``DEV_CTRL``), disables + smart-wakeup with a read-modify-write of ``MISC_CTRL`` when the part + advertises that feature, and waits until ``STATUS`` reports + ``NORMAL``. +- ``close()`` sets the power-down bit in ``DEV_CTRL`` and verifies that + ``STATUS`` reports ``POWER_DOWN``. + +**Touch Samples**. Each reported contact uses the touch-area index as +its stable ``id``. Flags follow the common touchscreen conventions: + +- First contact: ``TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID | TOUCH_PRESSURE_VALID`` +- Continued contact: ``TOUCH_MOVE`` with the same validity bits +- Lost contact: ``TOUCH_UP | TOUCH_ID_VALID | TOUCH_POS_VALID`` at the + last known coordinates + +Supported gesture codes from the controller are mapped onto the common +``TOUCH_*`` gesture values (double-click and slide directions). + +**Application Notes**. + +- ``read()`` returns a variable-length sample. Buffers must be at least + ``SIZEOF_TOUCH_SAMPLE_S(maxpoint)`` bytes; reading only + ``sizeof(struct touch_sample_s)`` (one contact) desynchronizes the + stream when multiple fingers are down. +- The example under ``apps/examples/touchscreen`` currently assumes a + single-point sample size and is not suitable for multi-touch testing + without a larger read buffer. +- Header: ``include/nuttx/input/st7123.h`` +- Driver: ``drivers/input/st7123.c`` diff --git a/Documentation/components/drivers/character/touchscreen.rst b/Documentation/components/drivers/character/touchscreen.rst index ce47444ed2c0c..627260bd7211e 100644 --- a/Documentation/components/drivers/character/touchscreen.rst +++ b/Documentation/components/drivers/character/touchscreen.rst @@ -41,14 +41,14 @@ Application Programming Interface ================================= The first thing to be done in order to use the touchscreen driver from an -application is to include the correct header filer. It contains the +application is to include the correct header filer. It contains the Application Programming Interface to the driver. To do so, include .. code-block:: c #include -Touchscreen driver is registered as a POSIX character device file into +Touchscreen driver is registered as a POSIX character device file into ``/dev`` namespace. It is necessary to open the device to get a file descriptor for further operations. This can be done with standard POSIX ``open()`` call. @@ -63,4 +63,11 @@ This command let the current handle has the device grabbed. When a handle grabs a device it becomes sole recipient for all touchscreen events coming from the device. An argument is an ``int32_t`` variable to enable or disable the grab. +Supported Controllers +===================== +Individual touchscreen controller drivers are documented under +:doc:`input/index`. Controllers currently covered there include: + +- :doc:`input/st7123` — ST7123 (and related ST7121) capacitive multi-touch + controller over I2C