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