nuttx/drivers/input/st7123.c
Filipe Cavalcanti 1e06783d83 drivers/input: support ST7123 touchscreen controller
Add support for ST7123 touchscreen controller (I2C only).
It requires board level init to register a callback, as polling
mode is not supported.

Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
2026-08-06 12:42:15 +02:00

1002 lines
29 KiB
C

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