xiaoxiang781216 commented on code in PR #19674: URL: https://github.com/apache/nuttx/pull/19674#discussion_r3708935608
########## drivers/input/st7123.c: ########## @@ -0,0 +1,978 @@ +/**************************************************************************** + * 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 <nuttx/sched.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <debug.h> +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/bits.h> +#include <nuttx/wqueue.h> +#include <nuttx/input/touchscreen.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; + 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]; +}; + +/**************************************************************************** + * 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(void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Global sample buffer */ + +static uint8_t g_sample_buf[SIZEOF_TOUCH_SAMPLE_S(ST7123_MAX_TOUCH_AREAS)]; + +static struct st7123_dev_t g_st7123_dev = + { + .lower = + { + .maxpoint = 1, + .xres = 0, + .yres = 0, + .priv = NULL, + .control = st7123_control, + .write = st7123_write, + .open = st7123_open, + .close = st7123_close, + }, + .i2c = NULL, + .fw_version = 0, + .downmap = 0, + .lastx = + { + 0 + }, + .lasty = + { + 0 + }, + }; + +/**************************************************************************** + * 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(void *arg) +{ + struct st7123_dev_t *dev = (struct st7123_dev_t *) arg; + struct touch_lowerhalf_s *lower = &dev->lower; + FAR struct touch_sample_s *sample = + (FAR struct touch_sample_s *) g_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(g_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; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: st7123_interrupt_callback + * + * Description: + * Board-level interrupt callback for the ST7123. Interrupts that arrive + * before the device has been registered are dropped because the upper + * half and I2C state do not exist yet. Otherwise the touch frame is + * fetched from the high-priority work queue. + * + * Input Parameters: + * arg - Unused; the global device instance is used instead + * + ****************************************************************************/ + +void st7123_interrupt_callback(void *arg) +{ + if (g_st7123_dev.lower.priv == NULL) + { + return; + } + + work_queue(HPWORK, &g_st7123_dev.work, st7123_data_worker, + &g_st7123_dev, 0); +} + +/**************************************************************************** + * Name: st7123_register + * + * Description: + * Probe the ST7123 over I2C, configure the touchscreen lower half from + * the reported resolution and touch-area count, and register it as + * /dev/inputN. + * + * Input Parameters: + * i2c - I2C master used to talk to the ST7123 + * minor - Device minor number used to form /dev/inputN + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int st7123_register(struct i2c_master_s *i2c, uint8_t minor) +{ + DEBUGASSERT(i2c != NULL); + + char devname[DEV_NAMELEN]; + struct st7123_dev_t *priv = &g_st7123_dev; Review Comment: why not kmm_zalloc ########## drivers/input/st7123.c: ########## @@ -0,0 +1,978 @@ +/**************************************************************************** + * 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 <nuttx/sched.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <debug.h> +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/bits.h> +#include <nuttx/wqueue.h> +#include <nuttx/input/touchscreen.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; + 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]; +}; + +/**************************************************************************** + * 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(void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Global sample buffer */ + +static uint8_t g_sample_buf[SIZEOF_TOUCH_SAMPLE_S(ST7123_MAX_TOUCH_AREAS)]; Review Comment: should we move to st7123_dev_t ########## include/nuttx/input/st7123.h: ########## @@ -0,0 +1,103 @@ +/**************************************************************************** + * 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 and interrupt entry points 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> + +#ifdef CONFIG_INPUT_ST7123 + +/**************************************************************************** + * 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, and register it as + * /dev/inputN where N is the given minor number. + * + * 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 + * + * 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); + +/**************************************************************************** + * Name: st7123_interrupt_callback + * + * Description: + * Board-level interrupt callback for the ST7123. The board GPIO ISR + * should call this function when the controller asserts its interrupt + * line. Interrupts that arrive before st7123_register() has completed + * are ignored. Otherwise a high-priority work-queue job is scheduled to + * read the touch frame and deliver it to the touchscreen upper half. + * + * Input Parameters: + * arg - Unused; the driver uses its global device instance + * + ****************************************************************************/ + +void st7123_interrupt_callback(FAR void *arg); Review Comment: who invoke st7123_interrupt_callback ########## drivers/input/st7123.c: ########## @@ -0,0 +1,978 @@ +/**************************************************************************** + * 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 <nuttx/sched.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <debug.h> +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/bits.h> +#include <nuttx/wqueue.h> +#include <nuttx/input/touchscreen.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; + 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]; +}; + +/**************************************************************************** + * 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(void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Global sample buffer */ + +static uint8_t g_sample_buf[SIZEOF_TOUCH_SAMPLE_S(ST7123_MAX_TOUCH_AREAS)]; + +static struct st7123_dev_t g_st7123_dev = Review Comment: remove g_st7123_dev ########## drivers/input/st7123.c: ########## @@ -0,0 +1,978 @@ +/**************************************************************************** + * 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 <nuttx/sched.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <debug.h> +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/bits.h> +#include <nuttx/wqueue.h> +#include <nuttx/input/touchscreen.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; + 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]; +}; + +/**************************************************************************** + * 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(void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Global sample buffer */ + +static uint8_t g_sample_buf[SIZEOF_TOUCH_SAMPLE_S(ST7123_MAX_TOUCH_AREAS)]; + +static struct st7123_dev_t g_st7123_dev = + { + .lower = + { + .maxpoint = 1, + .xres = 0, + .yres = 0, + .priv = NULL, + .control = st7123_control, + .write = st7123_write, + .open = st7123_open, + .close = st7123_close, + }, + .i2c = NULL, + .fw_version = 0, + .downmap = 0, + .lastx = + { + 0 + }, + .lasty = + { + 0 + }, + }; + +/**************************************************************************** + * 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(void *arg) +{ + struct st7123_dev_t *dev = (struct st7123_dev_t *) arg; + struct touch_lowerhalf_s *lower = &dev->lower; + FAR struct touch_sample_s *sample = + (FAR struct touch_sample_s *) g_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(g_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; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: st7123_interrupt_callback + * + * Description: + * Board-level interrupt callback for the ST7123. Interrupts that arrive + * before the device has been registered are dropped because the upper + * half and I2C state do not exist yet. Otherwise the touch frame is + * fetched from the high-priority work queue. + * + * Input Parameters: + * arg - Unused; the global device instance is used instead + * + ****************************************************************************/ + +void st7123_interrupt_callback(void *arg) +{ + if (g_st7123_dev.lower.priv == NULL) + { + return; + } + + work_queue(HPWORK, &g_st7123_dev.work, st7123_data_worker, + &g_st7123_dev, 0); +} + +/**************************************************************************** + * Name: st7123_register + * + * Description: + * Probe the ST7123 over I2C, configure the touchscreen lower half from + * the reported resolution and touch-area count, and register it as + * /dev/inputN. + * + * Input Parameters: + * i2c - I2C master used to talk to the ST7123 + * minor - Device minor number used to form /dev/inputN + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int st7123_register(struct i2c_master_s *i2c, uint8_t minor) +{ + DEBUGASSERT(i2c != NULL); Review Comment: move after line 920 ########## drivers/input/st7123.c: ########## @@ -0,0 +1,978 @@ +/**************************************************************************** + * 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 <nuttx/sched.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <debug.h> +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/bits.h> +#include <nuttx/wqueue.h> +#include <nuttx/input/touchscreen.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; + 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]; +}; + +/**************************************************************************** + * 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(void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Global sample buffer */ + +static uint8_t g_sample_buf[SIZEOF_TOUCH_SAMPLE_S(ST7123_MAX_TOUCH_AREAS)]; + +static struct st7123_dev_t g_st7123_dev = + { + .lower = + { + .maxpoint = 1, + .xres = 0, + .yres = 0, + .priv = NULL, + .control = st7123_control, + .write = st7123_write, + .open = st7123_open, + .close = st7123_close, + }, + .i2c = NULL, + .fw_version = 0, + .downmap = 0, + .lastx = + { + 0 + }, + .lasty = + { + 0 + }, + }; + +/**************************************************************************** + * 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(void *arg) +{ + struct st7123_dev_t *dev = (struct st7123_dev_t *) arg; + struct touch_lowerhalf_s *lower = &dev->lower; + FAR struct touch_sample_s *sample = + (FAR struct touch_sample_s *) g_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(g_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; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: st7123_interrupt_callback + * + * Description: + * Board-level interrupt callback for the ST7123. Interrupts that arrive + * before the device has been registered are dropped because the upper + * half and I2C state do not exist yet. Otherwise the touch frame is + * fetched from the high-priority work queue. + * + * Input Parameters: + * arg - Unused; the global device instance is used instead + * + ****************************************************************************/ + +void st7123_interrupt_callback(void *arg) +{ + if (g_st7123_dev.lower.priv == NULL) + { + return; + } + + work_queue(HPWORK, &g_st7123_dev.work, st7123_data_worker, + &g_st7123_dev, 0); +} + +/**************************************************************************** + * Name: st7123_register + * + * Description: + * Probe the ST7123 over I2C, configure the touchscreen lower half from + * the reported resolution and touch-area count, and register it as + * /dev/inputN. + * + * Input Parameters: + * i2c - I2C master used to talk to the ST7123 + * minor - Device minor number used to form /dev/inputN + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int st7123_register(struct i2c_master_s *i2c, uint8_t minor) +{ + DEBUGASSERT(i2c != NULL); + + char devname[DEV_NAMELEN]; + struct st7123_dev_t *priv = &g_st7123_dev; + st7123_status_t status; + int ret; + + priv->i2c = i2c; + priv->i2c_config = (struct i2c_config_s) Review Comment: let's assign one by one -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
